Skip to content

Comments

feat: yield bearing bridge#177

Open
waelsy123 wants to merge 28 commits intomainfrom
feat/yield-bearing-bridge-full
Open

feat: yield bearing bridge#177
waelsy123 wants to merge 28 commits intomainfrom
feat/yield-bearing-bridge-full

Conversation

@waelsy123
Copy link
Contributor

@waelsy123 waelsy123 commented Feb 6, 2026

a full feat branch for yield bearing bridge, this PR replace #126 & #118

  • add master vault
  • add YBB gateways and integrate that with the bridge
  • happy path e2e test for deposit
  • cover master vault with tests

@waelsy123 waelsy123 requested review from godzillaba and gzeoneth and removed request for gzeoneth February 6, 2026 16:59
Copy link
Member

@gzeoneth gzeoneth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still think the fee toggle just add complexity without good usecase

Comment on lines 229 to 230
address masterVaultRoles,
address masterVaultBeaconProxyFactory
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

breaks CREATE2-like deterministic deployment behavior as these new param are not part of _getL1Salt and _getL2Salt; we should consider to deploy these contract in the factory instead of taking untrusted input

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* @title Layer 1 Gateway contract for bridging standard ERC20s with YBB enabled
* @notice Escrows funds into MasterVaults for yield bearing bridging.
*/
contract L1YbbERC20Gateway is L1ERC20Gateway {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L2 token created with underlying token decimals, but MV share have +6 decimals and L2 token represent MV share instead of underlying so we need to adjust somewhere

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/// - Set the rebalance cooldown
bytes32 public constant GENERAL_MANAGER_ROLE = keccak256("GENERAL_MANAGER_ROLE");
/// @notice The fee manager can:
/// - Toggle performance fees on/off
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider to take this away from FEE_MANAGER_ROLE because the current implementation allow reseting the high water mark by toggling the fee off and on. If the fee manager can temporally manipulate sub-vault price it can drain the vault too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.mulDiv(1e18, totalSupply(), MathUpgradeable.Rounding.Up)
);
} else {
_distributePerformanceFee();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can revert (if subvault.withdraw revert), preventing disabling fee when subvault have withdrawal paused

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


__ReentrancyGuard_init();
__Pausable_init();
__MasterVaultRoles_init();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

local roles are not initialized and no one have local default admin role to grant roles

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* @title Layer 1 Gateway contract for bridging Custom ERC20s with YBB enabled
* @notice Escrows funds into MasterVaults for yield bearing bridging.
*/
contract L1YbbCustomGateway is L1CustomGateway {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lack fee token (Orbit) variant, same for the L1YbbERC20Gateway

Comment on lines 297 to 303
uint256 totalAssetsUp = _totalAssetsLessProfit(MathUpgradeable.Rounding.Up);
uint256 totalAssetsDown = _totalAssetsLessProfit(MathUpgradeable.Rounding.Down);
uint256 idleTargetUp =
totalAssetsUp.mulDiv(1e18 - targetAllocationWad, 1e18, MathUpgradeable.Rounding.Up);
uint256 idleTargetDown =
totalAssetsDown.mulDiv(1e18 - targetAllocationWad, 1e18, MathUpgradeable.Rounding.Down);
uint256 idleBalance = asset.balanceOf(address(this));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inconsistent use of _totalAssetsLessProfit and asset.balanceOf(address(this))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you explain more what you mean? i'm not sure i follow

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're changing rebalancing to include profit assets too fwiw

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (idleAssets < assets) {
uint256 assetsToWithdraw = assets - idleAssets;
// slither-disable-next-line unused-return
subVault.withdraw(assetsToWithdraw, address(this), address(this));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also lack subvault slippage check tho idk what is a good fix

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one of the assumption we have here is subvault should be erc4626 compliant, which require subvault.withdraw() to transfer exactly the requested assets a nd if a subVault misbehaves safeTransfer method on the next line would revert anyway

/// @notice Set the target allocation of assets to keep in the subvault
/// @dev Target allocation must be between 0 and 1e18 (100%).
/// @param _targetAllocationWad The target allocation in wad (1e18 = 100%)
function setTargetAllocationWad(uint64 _targetAllocationWad)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lack event

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onlyGateway
returns (uint256 shares)
{
shares = _convertToSharesRoundDown(assets);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should check shares != 0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo we should implement an optional slippage check in the gateways

we could pass it by repurposing extraData which would leave the existing function signature unchanged. we could also introduce a new function which is probably a bit better

Copy link
Contributor

@godzillaba godzillaba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still reviewing just wanted to leave early comments

Comment on lines +31 to +33
MasterVaultRoles _rolesRegistry = new MasterVaultRoles();
_rolesRegistry.initialize(_admin);
rolesRegistry = _rolesRegistry;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the roles registry should be behind a Transparent proxy

l1Deployment.standardGateway = ybbResult.standardGateway;
l1Deployment.customGateway = ybbResult.customGateway;
} else {
// Delegate standard gateway deployment to library
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i feel like we might be able to rework this external lib to make this diff easier to verify.

i wonder if we could simply copy paste all the existing code into a single external library function instead of breaking it up?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like copy-paste all createTokenBridge method into the ext lib? it was a challenging task to move some of this logic out of this contract while trying to reduce the diff. first I tried to have only ybb related stuff to be in the ext lib but then I ran into stack too deep issues. so I decided to move the deployment logic into that library

Comment on lines +57 to +60
function _setMasterVaultFactory(address _masterVaultFactory) internal {
require(_masterVaultFactory != address(0), "BAD_MASTER_VAULT_FACTORY");
masterVaultFactory = _masterVaultFactory;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bit of a nit, but even though it's a sortof convention in this codebase idk if an address(0) check is very useful

if we keep it though i think it should inlined in the initializer

Comment on lines +50 to +58
uint256 prevBalance = IERC20(_l1Token).balanceOf(address(this));
IERC20(_l1Token).safeTransferFrom(_from, address(this), _amount);
uint256 postBalance = IERC20(_l1Token).balanceOf(address(this));
amountReceived = postBalance - prevBalance;

address masterVault = IMasterVaultFactory(masterVaultFactory).getVault(_l1Token);
IERC20(_l1Token).safeIncreaseAllowance(masterVault, amountReceived);
amountReceived = IMasterVault(masterVault).deposit(amountReceived);
require(amountReceived > 0, "ZERO_SHARES");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this code is copy pasted a few times, it's probably better to separate this out into a lib or inherited contract or something

onlyGateway
returns (uint256 shares)
{
shares = _convertToSharesRoundDown(assets);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo we should implement an optional slippage check in the gateways

we could pass it by repurposing extraData which would leave the existing function signature unchanged. we could also introduce a new function which is probably a bit better

if (idleAssets < assets) {
uint256 assetsToWithdraw = assets - idleAssets;
// slither-disable-next-line unused-return
subVault.withdraw(assetsToWithdraw, address(this), address(this));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah imo this is okay, could use some documentation though

// L1 logic contracts shared by all token bridges
L1Templates public l1Templates;

YbbL1Templates public ybbL1Templates;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's move this to the end so we don't shift the storage layout

/**
* @notice Set addresses of YBB L1 logic contracts (standard, custom, fee-token-based, and vault templates).
*/
function setYbbTemplates(YbbL1Templates calldata _ybbL1Templates) external onlyOwner {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this should be part of the main setTemplates so it isn't forgotten about

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants