# YAdapter
YAdapter is an attempt to build a cross-chain strategy based on Yearn V3 and the Tokenized strategy. It can take deposits on a *SourceChain* and deposit them to a different *⁄Destination* chain. This proof of concept (POC) deposits the funds by simply depositing the asset into a Yearn vault on the Destination side, but the design would allow for more complex strategies as well.
# Contracts
YAdapter comprises four contracts designed to facilitate cross-chain deposits.
## Origin Strategy
The entry point of a cross-chain strategy is the Origin Strategy. This is where the user makes deposits. The strategy keeps a part of the deposit idle to enable easy withdrawals but bridges most of the capital to the Destination Adapter so it can be utilized at better yield opportunities.
## Bridge
Two simple interfaces can be implemented to build a bridge that is compatible with the yAdapter. Any bridge technology that can implement these interfaces can serve the strategy. The POC implements the Connext bridge by providing ConnextOriginBridge and ConnextOriginDestinationBridge to move funds with connext.
### Origin Bridge
The Origin Bridge is an interface that the Origin Strategy uses to handle the bridging of funds. The operations deposit and withdraw are asynchronous, given the nature of a bridge. By using getDepositFee and getWithdrawalFee, the strategy can obtain information about the fees that have to be paid to the bridge.
```
interface IOriginBridge {
function deposit(
address receiver,
address token,
uint256 _amount
) external payable;
function withdraw(address token, uint256 _amount) external payable;
function getDepositFee(
address token,
uint256 _amount
) external returns (address, uint256);
function getWithdrawlFee(
address token,
uint256 _amount
) external returns (address, uint256);
}
```
### Destination Bridge
The counterpart of the Origin Bridge is the Destination Bridge. It receives bridged funds and sends them to the corresponding adapter. The bridge implementation could be part of the Adapter contract, but to maintain a separation of concerns, they are kept separate.
```
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.18;
interface IDestinationBridge {
function redeem(
address receiver,
address token,
uint256 _amount
) external payable returns (uint);
function getRedeemFee(
address token,
uint256 _amount
) external returns (address, uint256);
}
```
## Destination Adapter
It receives the funds on the Destination side and deposits them. For the POC, it just deposits into a Yearn vault, but Adapters doing more advanced operations are also feasible.
## Callbacks
The strategy eventually needs to react to bridge actions. Hence they are async and triggered by the bridge they could impelemt two simple interfaces two react to bridge actions
### IBridgeReceiver
Can be implemented to react to incoming transfers. It is used here to update the bridgedAssets variable.
```
interface IBridgeReceiver {
function onFundsReceivedCallback(
address token,
uint amount,
uint left
) external;
}
```
### IBridgeSender
Can be implemented to free up funds on demand. It is used in the Destination strategy to withdraw funds from the adapter and send them back to the Origin.
```
interface IBridgeSender {
function onFundsRequested(
address token,
uint amount
) external returns (uint);
}
```
# Implementation
## OriginStrategy
The core concept of the origin strategy is to maintain an 80/20 ratio between bridged assets and idle assets. This should make it always possible to withdraw from the strategy while having most of the funds deposited to earn yield. To deposit and withdraw, the keeper can use depositFunds() and requestWithdraw(). The funds are then moved by the bridge asynchronously.
### depositFunds
Can be called by a keeper to deposit funds into the bridge. Note that the keeper has to provide a sufficient amount of ETH to cover the relayer fees. The amount needed can be retrieved from the Connext SDK.
```
function depositFunds(uint256 _amount) external payable onlyKeepers {
require(destinationBridge != address(0), "adapter is zero");
//grant bridge the allowance to take asset from strat
asset.increaseAllowance(address(bridge), _amount);
//send fund to bridge
//Keeper has to provide suffiecnt ETH via msg.sender to cover the relayer fee
//The current rate can be requested offchain by using the SDK of the bridge
bridge.deposit{value: msg.value}(
destinationBridge,
address(asset),
_amount
);
//reset allowance
asset.increaseAllowance(address(bridge), 0);
//account bridge asssets
bridgedAssets += _amount;
}
```
### requestWithdrawl
Sends a request to withdraw a specified amount from the bridge. After the funds have been sent by the bridge, the onFundsReceived callback will be invoked, allowing the strategy to account for them.
````
function requestWithdrawl(uint256 _amount) external payable onlyKeepers {
bridge.withdraw{value: msg.value}(address(asset), _amount);
}
````
### onFundsReceivedCallback
Invoked when the strategy has received funds from the bridge. The calldata contains the amount that is left at the bridge.
````
function onFundsReceivedCallback(
address _asset,
uint _amount,
bytes calldata _callData
) external onlyBridge {
bridgedAssets = abi.decode(_callData, (uint256));
}
````
## Deposit Diagramm

## Withdraw Diagramm

# Future Vision
The submission contains a Proof of Concept illustrating how a cross-chain strategy for Yearn V3 vaults could look. It was built to provide a bridge-agnostic method for moving funds from an origin to a destination chain. The strategy is designed to be compatible with any ERC-4626-compliant source of yield, enabling vaults utilizing this strategy to deploy their capital on chains that might yield better returns.
However, there are items that need to be resolved before turning the strategy into production.
## Testing
The POC contains a basic test suite that ensures the implementation works as expected. However, given its complexity, it requires a lot more tests to ensure that funds are always secure and that eventual edge cases are handled properly.
## Bridge research
It would make sense to research edge cases that can occur during bridging and how to mitigate them. I also believe it would be beneficial to seek a review from Connext to gather their input.
## Keeper
In the POC, the strategy allocates funds based on an 80/20 ratio. The keeper's objective is to maintain this ratio. The keeper could use the functions getDepositAmount() and getWithdrawAmount() on the origin chain. However, the keeper might obtain more accurate estimations when having access to both source and destination RPCs. It would also be important to have access to the Connext JS SDK to get the current fee amount.