diff --git a/Makefile b/Makefile index e2b83eb..d80fdd8 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ inspect :; forge inspect ${contract} storage-layout --pretty FORK_URL := ${ETH_RPC_URL} -test :; forge test -vv --fork-url ${FORK_URL} +tests :; forge test -vv --fork-url ${FORK_URL} trace :; forge test -vvv --fork-url ${FORK_URL} gas :; forge test --fork-url ${FORK_URL} --gas-report test-contract :; forge test -vv --match-contract $(contract) --fork-url ${FORK_URL} diff --git a/README.md b/README.md index 1fc418e..7a83e51 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,26 @@ # Yearn Stake the Bridge -![alt text](image.png) +![alt text](setup.png) # Structure ## [L1 Deployer](https://github.com/yearn/yearn-stb/blob/master/src/L1Deployer.sol) +- Allows anyone to add a new asset to any valid Rollup +- Allows for a Rollup Admin to specify its specific EScrow Manager +- Allows for a Rollup Admin to add custom vaults for a specific asset. +- Deploys vaults and does full setup for any new assets added. + ## [L2 Deployer](https://github.com/yearn/yearn-stb/blob/master/src/L2Deployer.sol) +- Receives message from L1 Deployer when a new escrow was created for a new asset. +- Deploys L2 Token, Escrow and convertor and completes setup +- Owned by L2 Admin ## [L1 Yearn Escrow](https://github.com/yearn/yearn-stb/blob/master/src/L1YearnEscrow.sol) - +- Yearn specific L1 Escrow contract that handles bridge txns +- Will deposit funds into a Yearn vault over any set `minimumBuffer` +- If withdraws cannot be processed it will send shares to users when bridging back to L1. +- Rollups Admin can update the `minimumBuffer` as well as the vault it uses. ## Documentation @@ -21,19 +32,25 @@ https://book.getfoundry.sh/ ### Build ```shell -$ forge build +$ make build ``` ### Test ```shell -$ forge test +$ make tests +``` + +### Trace + +```shell +$ make trace ``` ### Gas Snapshots ```shell -$ forge snapshot +$ make snapshot ``` ### Cast diff --git a/setup.png b/setup.png new file mode 100644 index 0000000..b108e71 Binary files /dev/null and b/setup.png differ diff --git a/src/L1Deployer.sol b/src/L1Deployer.sol index 2ce01fe..404e5f1 100644 --- a/src/L1Deployer.sol +++ b/src/L1Deployer.sol @@ -12,16 +12,19 @@ contract L1Deployer is RoleManager { event RegisteredNewRollup( uint32 indexed rollupID, address indexed rollupContract, - address indexed manager + address indexed escrowManager ); - event UpdateRollupManager(uint32 indexed rollupID, address indexed manager); + event UpdateEscrowManager( + uint32 indexed rollupID, + address indexed escrowManager + ); event NewL1Escrow(uint32 indexed rollupID, address indexed l1Escrow); struct ChainConfig { IPolygonRollupContract rollupContract; - address manager; + address escrowManager; mapping(address => address) escrows; } @@ -34,7 +37,7 @@ contract L1Deployer is RoleManager { /// @notice Check if the msg sender is governance or the specified position holder. function _isRollupAdmin(uint32 _rollupID) internal view virtual { require( - msg.sender == chainConfig[_rollupID].rollupContract.admin(), + msg.sender == _chainConfig[_rollupID].rollupContract.admin(), "!admin" ); } @@ -50,7 +53,7 @@ contract L1Deployer is RoleManager { //////////////////////////////////////////////////////////////*/ /// @notice Mapping of chain ID to the rollup config. - mapping(uint32 => ChainConfig) public chainConfig; + mapping(uint32 => ChainConfig) internal _chainConfig; constructor( address _governator, @@ -82,56 +85,33 @@ contract L1Deployer is RoleManager { ); } - function registerRollup( - uint32 _rollupID, - address _l1Manager - ) external virtual { - ChainConfig storage _chainConfig = chainConfig[_rollupID]; - require( - address(_chainConfig.rollupContract) == address(0), - "registered" - ); - require(_l1Manager != address(0), "ZERO ADDRESS"); - - IPolygonRollupContract _rollupContract = rollupManager - .rollupIDToRollupData(_rollupID) - .rollupContract; - // Checks the rollup ID is valid and the caller is the rollup Admin. - require(msg.sender == _rollupContract.admin(), "!admin"); - - _chainConfig.rollupContract = _rollupContract; - _chainConfig.manager = _l1Manager; - - emit RegisteredNewRollup( - _rollupID, - address(_rollupContract), - _l1Manager - ); - } - - function updateRollupManager( - uint32 _rollupID, - address _l1Manager - ) external virtual onlyRollupAdmin(_rollupID) { - require(_l1Manager != address(0), "ZERO ADDRESS"); - chainConfig[_rollupID].manager = _l1Manager; - - emit UpdateRollupManager(_rollupID, _l1Manager); - } - /*////////////////////////////////////////////////////////////// ESCROW CREATION //////////////////////////////////////////////////////////////*/ + /** + * @notice Deploy a new L1 escrow contract for a specific rollup. + * @dev This will also trigger the L2 deployer to deploy deploy all needed + * contracts for the new bridged asset. + * + * This will register the rollup internally if not yet done. + * + * This will deploy a new Yearn vault and do the full setup if a default version + * is not yet deployed. + * + * @param _rollupID The rollups ID + * @param _asset The asset to bridge to the rollup. + * @return _l1Escrow The address of the rollup specific l1 Escrow. + * @return _vault The Yearn vault the escrow will deposit into. + */ function newEscrow( uint32 _rollupID, address _asset ) external virtual returns (address _l1Escrow, address _vault) { - // Verify the rollup Id is valid. - require( - address(chainConfig[_rollupID].rollupContract) != address(0), - "rollup not registered" - ); + // Register rollup if not already done. Verifies its a valid rollup ID. + if (getRollupContract(_rollupID) == address(0)) { + _registerRollup(_rollupID, address(0)); + } // Verify that the vault is not already set for that chain. _l1Escrow = getEscrow(_rollupID, _asset); @@ -149,31 +129,123 @@ contract L1Deployer is RoleManager { _l1Escrow = _deployL1Escrow(_rollupID, _asset, _vault); } + /*////////////////////////////////////////////////////////////// + ROLLUP MANAGEMENT + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Register a rollup with this deployer contract. + * @dev Only a rollups Admin can set the `_escrowManager` + * @param _rollupID ID for the rollup to register + * @param _escrowManager Address to set as the L1 Manager. + */ + function registerRollup( + uint32 _rollupID, + address _escrowManager + ) external virtual { + require(getRollupContract(_rollupID) == address(0), "registered"); + _registerRollup(_rollupID, _escrowManager); + } + + /** + * @dev Registers a new rollup with the Deployer. + * This is called either manually or during the first {newEscrow} call. + */ + function _registerRollup( + uint32 _rollupID, + address _escrowManager + ) internal virtual { + ChainConfig storage chainConfig_ = _chainConfig[_rollupID]; + + IPolygonRollupContract _rollupContract = rollupManager + .rollupIDToRollupData(_rollupID) + .rollupContract; + + // Checks the rollup ID is valid + address admin = _rollupContract.admin(); + // If the caller is not the rollup Admin. + if (msg.sender != _rollupContract.admin()) { + // Default the manager to be the admin + _escrowManager = admin; + } + + chainConfig_.rollupContract = _rollupContract; + chainConfig_.escrowManager = _escrowManager; + + emit RegisteredNewRollup( + _rollupID, + address(_rollupContract), + _escrowManager + ); + } + + /** + * @notice Allows the Rollup Admin to change the L1 Manager. + * @param _rollupID ID for the rollup. + * @param _escrowManager New address to set as l1Manager in new escrows. + */ + function updateEscrowManager( + uint32 _rollupID, + address _escrowManager + ) external virtual onlyRollupAdmin(_rollupID) { + require(_escrowManager != address(0), "ZERO ADDRESS"); + _chainConfig[_rollupID].escrowManager = _escrowManager; + + emit UpdateEscrowManager(_rollupID, _escrowManager); + } + + /*////////////////////////////////////////////////////////////// + CUSTOM VAULTS + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Creates a new custom vault and escrow for a specific asset on the specified rollup. + * @param _rollupID The ID of the rollup. + * @param _asset The address of the asset for which the vault and escrow are created. + * @return _l1Escrow The address of the newly created L1 escrow. + * @return _vault The address of the newly created vault. + */ function newCustomVault( uint32 _rollupID, address _asset - ) external virtual onlyRollupAdmin(_rollupID) returns (address _vault) { + ) + external + virtual + onlyRollupAdmin(_rollupID) + returns (address _l1Escrow, address _vault) + { _vault = _newVault(_rollupID, _asset); - _newCustomVault(_rollupID, _asset, _vault); + _l1Escrow = _newCustomVault(_rollupID, _asset, _vault); } + /** + * @notice Creates a new custom vault for a specific asset on the specified rollup. + * @param _rollupID The ID of the rollup. + * @param _asset The address of the asset for which the vault is created. + * @param _vault The address of the vault. + * @return _l1Escrow The address of the newly created L1 escrow. + */ function newCustomVault( uint32 _rollupID, address _asset, address _vault - ) external virtual onlyRollupAdmin(_rollupID) { + ) external virtual onlyRollupAdmin(_rollupID) returns (address _l1Escrow) { if (!isVaultsRoleManager(_vault)) { _addNewVault(_rollupID, _vault); } - _newCustomVault(_rollupID, _asset, _vault); + _l1Escrow = _newCustomVault(_rollupID, _asset, _vault); } + /** + * @dev Deploys an L1 Escrow for a custom vault if one does not exist. + * Will store all relevant information as well. + */ function _newCustomVault( uint32 _rollupID, address _asset, address _vault - ) internal virtual { - address _l1Escrow = getEscrow(_rollupID, _asset); + ) internal virtual returns (address _l1Escrow) { + _l1Escrow = getEscrow(_rollupID, _asset); if (_l1Escrow == address(0)) { _l1Escrow = _deployL1Escrow(_rollupID, _asset, _vault); @@ -186,18 +258,23 @@ contract L1Deployer is RoleManager { ESCROW CREATION //////////////////////////////////////////////////////////////*/ + /** + * @dev Deploys a new L1 Escrow and send a message to the bridge to + * tell the L2 deployer to deploy the needed contract on the L2 + */ function _deployL1Escrow( uint32 _rollupID, address _asset, address _vault ) internal returns (address _l1Escrow) { - ChainConfig storage _chainConfig = chainConfig[_rollupID]; + ChainConfig storage chainConfig_ = _chainConfig[_rollupID]; + // Get the init data for the proxy implementation bytes memory data = abi.encodeCall( L1YearnEscrow.initialize, ( - _chainConfig.rollupContract.admin(), - _chainConfig.manager, + chainConfig_.rollupContract.admin(), + chainConfig_.escrowManager, address(polygonZkEVMBridge), getL2EscrowAddress(_asset), _rollupID, @@ -207,8 +284,10 @@ contract L1Deployer is RoleManager { ) ); + // Cache to double check we deploy to the right address. address expectedL1Escrow = getL1EscrowAddress(_asset); + // Deploy the new escrow and initialize _l1Escrow = _create3Deploy( keccak256(abi.encodePacked(bytes("L1Escrow:"), _asset)), getPositionHolder(ESCROW_IMPLEMENTATION), @@ -219,10 +298,9 @@ contract L1Deployer is RoleManager { require(_l1Escrow == expectedL1Escrow, "wrong address"); // Set the mapping - _chainConfig.escrows[_asset] = _l1Escrow; + chainConfig_.escrows[_asset] = _l1Escrow; // Send Message to Bridge for L2 - // TODO: Will L2 Deployer be the same each chain? polygonZkEVMBridge.bridgeMessage( _rollupID, getPositionHolder(L2_DEPLOYER), @@ -238,10 +316,32 @@ contract L1Deployer is RoleManager { emit NewL1Escrow(_rollupID, _l1Escrow); } + /*////////////////////////////////////////////////////////////// + GETTER FUNCTIONS + //////////////////////////////////////////////////////////////*/ + + /** + * @dev Returns the address of the rollup contract associated with the specified rollup ID. + * @param _rollupID The ID of the rollup. + * @return The address of the rollup contract. + */ + function getRollupContract(uint32 _rollupID) public view returns (address) { + return address(_chainConfig[_rollupID].rollupContract); + } + + /** + * @dev Returns the address of the escrow manager associated with the specified rollup. + * @param _rollupID The ID of the rollup. + * @return The address of the escrow manager. + */ + function getEscrowManager(uint32 _rollupID) public view returns (address) { + return _chainConfig[_rollupID].escrowManager; + } + /** - * @notice Get the L1 Escrow for a specific asset and chain ID. + * @notice Get the L1 Escrow for a specific asset and rollup ID. * @dev This will return address(0) if one has not been added or deployed. - * @param _rollupID The rollup chain ID. + * @param _rollupID The ID of the rollup. * @param _asset The underlying asset used. * @return The Escrow for the specified `_asset` and `_rollupID`. */ @@ -249,6 +349,6 @@ contract L1Deployer is RoleManager { uint32 _rollupID, address _asset ) public view virtual returns (address) { - return chainConfig[_rollupID].escrows[_asset]; + return _chainConfig[_rollupID].escrows[_asset]; } } diff --git a/src/L2Deployer.sol b/src/L2Deployer.sol index 0052fcd..e4454b4 100644 --- a/src/L2Deployer.sol +++ b/src/L2Deployer.sol @@ -7,8 +7,7 @@ import {L2Escrow} from "@zkevm-stb/L2Escrow.sol"; import {L2TokenConverter} from "@zkevm-stb/L2TokenConverter.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -// Array of tokens? -// m +/// @title Polygon CDK Stake the Bridge L2 Deployer. contract L2Deployer is DeployerBase { event NewToken( address indexed l1Token, diff --git a/src/RoleManager.sol b/src/RoleManager.sol index 6a1d090..b4e5606 100644 --- a/src/RoleManager.sol +++ b/src/RoleManager.sol @@ -10,7 +10,7 @@ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {DebtAllocatorFactory} from "@vault-periphery/debtAllocators/DebtAllocatorFactory.sol"; -/// @title PolyYearn Stake the Bridge Role Manager. +/// @title Yearn Stake the Bridge Role Manager. contract RoleManager is DeployerBase { /// @notice Revert message for when a contract has already been deployed. error AlreadyDeployed(address _contract); @@ -39,7 +39,7 @@ contract RoleManager is DeployerBase { address asset; uint32 rollupID; // 0 == default. address debtAllocator; - uint256 index; + uint96 index; } /*////////////////////////////////////////////////////////////// @@ -109,7 +109,7 @@ contract RoleManager is DeployerBase { _escrowImplementation ) { - chad = _czar; + chad = _governator; // Governator gets no roles. _setPositionHolder(GOVERNATOR, _governator); @@ -202,7 +202,7 @@ contract RoleManager is DeployerBase { asset: _asset, rollupID: _rollupID, debtAllocator: _debtAllocator, - index: vaults.length + index: uint96(vaults.length) }); // Add the vault to the mapping. @@ -378,7 +378,7 @@ contract RoleManager is DeployerBase { asset: _asset, rollupID: _rollupID, debtAllocator: _debtAllocator, - index: vaults.length + index: uint96(vaults.length) }); // Add the vault to the mapping. @@ -463,7 +463,7 @@ contract RoleManager is DeployerBase { */ function removeVault( address _vault - ) external virtual onlyPositionHolder(MANAGEMENT) { + ) external virtual onlyPositionHolder(CZAR) { // Get the vault specific config. VaultConfig memory config = vaultConfig[_vault]; // Make sure the vault has been added to the role manager. diff --git a/src/interfaces/Yearn/IEvents.sol b/src/interfaces/Yearn/IEvents.sol deleted file mode 100644 index c4046d9..0000000 --- a/src/interfaces/Yearn/IEvents.sol +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity >=0.8.18; - -interface IEvents { - /*////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - /** - * @notice Emitted when a strategy is shutdown. - */ - event StrategyShutdown(); - - /** - * @notice Emitted on the initialization of any new `strategy` that uses `asset` - * with this specific `apiVersion`. - */ - event NewTokenizedStrategy( - address indexed strategy, - address indexed asset, - string apiVersion - ); - - /** - * @notice Emitted when the strategy reports `profit` or `loss` and - * `performanceFees` and `protocolFees` are paid out. - */ - event Reported( - uint256 profit, - uint256 loss, - uint256 protocolFees, - uint256 performanceFees - ); - - /** - * @notice Emitted when the 'performanceFeeRecipient' address is - * updated to 'newPerformanceFeeRecipient'. - */ - event UpdatePerformanceFeeRecipient( - address indexed newPerformanceFeeRecipient - ); - - /** - * @notice Emitted when the 'keeper' address is updated to 'newKeeper'. - */ - event UpdateKeeper(address indexed newKeeper); - - /** - * @notice Emitted when the 'performanceFee' is updated to 'newPerformanceFee'. - */ - event UpdatePerformanceFee(uint16 newPerformanceFee); - - /** - * @notice Emitted when the 'management' address is updated to 'newManagement'. - */ - event UpdateManagement(address indexed newManagement); - - /** - * @notice Emitted when the 'emergencyAdmin' address is updated to 'newEmergencyAdmin'. - */ - event UpdateEmergencyAdmin(address indexed newEmergencyAdmin); - - /** - * @notice Emitted when the 'profitMaxUnlockTime' is updated to 'newProfitMaxUnlockTime'. - */ - event UpdateProfitMaxUnlockTime(uint256 newProfitMaxUnlockTime); - - /** - * @notice Emitted when the 'pendingManagement' address is updated to 'newPendingManagement'. - */ - event UpdatePendingManagement(address indexed newPendingManagement); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval( - address indexed owner, - address indexed spender, - uint256 value - ); - - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the `caller` has exchanged `assets` for `shares`, - * and transferred those `shares` to `owner`. - */ - event Deposit( - address indexed caller, - address indexed owner, - uint256 assets, - uint256 shares - ); - - /** - * @dev Emitted when the `caller` has exchanged `owner`s `shares` for `assets`, - * and transferred those `assets` to `receiver`. - */ - event Withdraw( - address indexed caller, - address indexed receiver, - address indexed owner, - uint256 assets, - uint256 shares - ); -} diff --git a/test/L1Deployer.t.sol b/test/L1Deployer.t.sol index dae55f8..912db89 100644 --- a/test/L1Deployer.t.sol +++ b/test/L1Deployer.t.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: AGPL-3.0 pragma solidity >=0.8.18; -import {Setup, console, L2Deployer, L1YearnEscrow, L2Token, L2Escrow, L2TokenConverter, IPolygonZkEVMBridge} from "./utils/Setup.sol"; +import {Setup, console, L1YearnEscrow, IPolygonZkEVMBridge, IVault, L1Deployer, ERC20} from "./utils/Setup.sol"; +import {IPolygonRollupManager, IPolygonRollupContract} from "../src/interfaces/Polygon/IPolygonRollupManager.sol"; contract L1DeployerTest is Setup { event BridgeEvent( @@ -15,7 +16,276 @@ contract L1DeployerTest is Setup { uint32 depositCount ); + event RegisteredNewRollup( + uint32 indexed rollupID, + address indexed rollupContract, + address indexed escrowManager + ); + + event UpdateEscrowManager( + uint32 indexed rollupID, + address indexed escrowManager + ); + + event NewL1Escrow(uint32 indexed rollupID, address indexed l1Escrow); + function setUp() public virtual override { super.setUp(); } + + function test_registerRollup_admin() public { + uint32 rollupID = 1; + assertEq(l1Deployer.getRollupContract(rollupID), address(0)); + assertEq(l1Deployer.getEscrowManager(rollupID), address(0)); + + assertEq(l1Deployer.getEscrow(rollupID, address(asset)), address(0)); + + address rollupContract = address( + IPolygonRollupManager(polygonZkEVMBridge.polygonRollupManager()) + .rollupIDToRollupData(rollupID) + .rollupContract + ); + address rollupAdmin = address( + IPolygonRollupManager(polygonZkEVMBridge.polygonRollupManager()) + .rollupIDToRollupData(rollupID) + .rollupContract + .admin() + ); + + vm.expectEmit(true, true, true, true, address(l1Deployer)); + emit RegisteredNewRollup(rollupID, rollupContract, czar); + vm.prank(rollupAdmin); + l1Deployer.registerRollup(rollupID, czar); + + assertEq(l1Deployer.getRollupContract(rollupID), rollupContract); + assertEq(l1Deployer.getEscrowManager(rollupID), czar); + assertEq(l1Deployer.getEscrow(rollupID, address(asset)), address(0)); + + vm.expectRevert(); + vm.prank(rollupAdmin); + l1Deployer.registerRollup(rollupID, governator); + + vm.expectRevert("!admin"); + vm.prank(czar); + l1Deployer.updateEscrowManager(rollupID, governator); + + vm.expectEmit(true, true, true, true, address(l1Deployer)); + emit UpdateEscrowManager(rollupID, governator); + vm.prank(rollupAdmin); + l1Deployer.updateEscrowManager(rollupID, governator); + + assertEq(l1Deployer.getRollupContract(rollupID), rollupContract); + assertEq(l1Deployer.getEscrowManager(rollupID), governator); + assertEq(l1Deployer.getEscrow(rollupID, address(asset)), address(0)); + } + + function test_registerRollup_rando() public { + uint32 rollupID = 1; + assertEq(l1Deployer.getRollupContract(rollupID), address(0)); + assertEq(l1Deployer.getEscrowManager(rollupID), address(0)); + assertEq(l1Deployer.getEscrow(rollupID, address(asset)), address(0)); + + address rollupContract = address( + IPolygonRollupManager(polygonZkEVMBridge.polygonRollupManager()) + .rollupIDToRollupData(rollupID) + .rollupContract + ); + address rollupAdmin = address( + IPolygonRollupManager(polygonZkEVMBridge.polygonRollupManager()) + .rollupIDToRollupData(rollupID) + .rollupContract + .admin() + ); + + vm.expectEmit(true, true, true, true, address(l1Deployer)); + emit RegisteredNewRollup(rollupID, rollupContract, rollupAdmin); + l1Deployer.registerRollup(rollupID, czar); + + assertEq(l1Deployer.getRollupContract(rollupID), rollupContract); + assertEq(l1Deployer.getEscrowManager(rollupID), rollupAdmin); + assertEq(l1Deployer.getEscrow(rollupID, address(asset)), address(0)); + + vm.expectRevert(); + vm.prank(rollupAdmin); + l1Deployer.registerRollup(rollupID, governator); + + vm.expectRevert("!admin"); + l1Deployer.updateEscrowManager(rollupID, governator); + + vm.expectEmit(true, true, true, true, address(l1Deployer)); + emit UpdateEscrowManager(rollupID, governator); + vm.prank(rollupAdmin); + l1Deployer.updateEscrowManager(rollupID, governator); + + assertEq(l1Deployer.getRollupContract(rollupID), rollupContract); + assertEq(l1Deployer.getEscrowManager(rollupID), governator); + assertEq(l1Deployer.getEscrow(rollupID, address(asset)), address(0)); + } + + function test_registerRollup_badIdea() public { + uint32 rollupID = 69; + assertEq(l1Deployer.getRollupContract(rollupID), address(0)); + assertEq(l1Deployer.getEscrowManager(rollupID), address(0)); + assertEq(l1Deployer.getEscrow(rollupID, address(asset)), address(0)); + + vm.expectRevert(); + l1Deployer.registerRollup(rollupID, czar); + } + + function test_newEscrow() public { + uint32 rollupID = 1; + assertEq(l1Deployer.getRollupContract(rollupID), address(0)); + assertEq(l1Deployer.getEscrowManager(rollupID), address(0)); + assertEq(l1Deployer.getEscrow(rollupID, address(asset)), address(0)); + assertEq(l1Deployer.getVault(address(asset)), address(0)); + + address rollupContract = address( + IPolygonRollupManager(polygonZkEVMBridge.polygonRollupManager()) + .rollupIDToRollupData(rollupID) + .rollupContract + ); + address rollupAdmin = address( + IPolygonRollupManager(polygonZkEVMBridge.polygonRollupManager()) + .rollupIDToRollupData(rollupID) + .rollupContract + .admin() + ); + + address _l1Escrow = getL1EscrowAddress(address(asset)); + + vm.expectEmit(true, true, true, true, address(l1Deployer)); + emit NewL1Escrow(rollupID, _l1Escrow); + (, address _vault) = l1Deployer.newEscrow(rollupID, address(asset)); + + // Rollup should be registered, vault and escrow deployed + assertEq(l1Deployer.getRollupContract(rollupID), rollupContract); + assertEq(l1Deployer.getEscrowManager(rollupID), rollupAdmin); + assertEq(l1Deployer.getEscrow(rollupID, address(asset)), _l1Escrow); + assertEq(l1Deployer.getVault(address(asset)), _vault); + + IVault vault = IVault(_vault); + + assertEq(vault.accountant(), address(accountant)); + assertEq(vault.asset(), address(asset)); + assertEq(vault.deposit_limit(), 2 ** 256 - 1); + ( + address asset_, + uint32 rollupID_, + address _debtAllocator, + uint96 _index + ) = l1Deployer.vaultConfig(_vault); + assertTrue(_debtAllocator != address(0)); + assertEq(_index, 0); + assertEq(rollupID_, 0); + assertEq(asset_, address(asset)); + + L1YearnEscrow escrow = L1YearnEscrow(_l1Escrow); + + assertEq(escrow.owner(), rollupAdmin); + assertTrue(escrow.hasRole(escrow.ESCROW_MANAGER_ROLE(), rollupAdmin)); + assertEq(escrow.polygonZkEVMBridge(), address(polygonZkEVMBridge)); + assertEq( + escrow.counterpartContract(), + getL2EscrowAddress(address(asset)) + ); + assertEq(escrow.counterpartNetwork(), rollupID); + assertEq(address(escrow.originTokenAddress()), address(asset)); + assertEq( + address(escrow.wrappedTokenAddress()), + getL2TokenAddress(address(asset)) + ); + assertEq(address(escrow.vaultAddress()), address(vault)); + assertEq(escrow.minimumBuffer(), 0); + assertEq( + asset.allowance(address(escrow), address(vault)), + 2 ** 256 - 1 + ); + } + + function test_customVault_preDeployed() public { + uint32 rollupID = 1; + assertEq(l1Deployer.getRollupContract(rollupID), address(0)); + assertEq(l1Deployer.getEscrowManager(rollupID), address(0)); + assertEq(l1Deployer.getEscrow(rollupID, address(asset)), address(0)); + assertEq(l1Deployer.getVault(address(asset)), address(0)); + + address rollupContract = address( + IPolygonRollupManager(polygonZkEVMBridge.polygonRollupManager()) + .rollupIDToRollupData(rollupID) + .rollupContract + ); + address rollupAdmin = address( + IPolygonRollupManager(polygonZkEVMBridge.polygonRollupManager()) + .rollupIDToRollupData(rollupID) + .rollupContract + .admin() + ); + + l1Deployer.registerRollup(rollupID, rollupAdmin); + + address _vault = vaultFactory.deploy_new_vault( + address(asset), + "test Vault", + "tsVault", + rollupAdmin, + 100 + ); + + vm.prank(rollupAdmin); + IVault(_vault).transfer_role_manager(address(l1Deployer)); + + address _l1Escrow = getL1EscrowAddress(address(asset)); + + vm.expectRevert("!admin"); + l1Deployer.newCustomVault(rollupID, address(asset), _vault); + + vm.expectEmit(true, true, true, true, address(l1Deployer)); + emit NewL1Escrow(rollupID, _l1Escrow); + vm.prank(rollupAdmin); + l1Deployer.newCustomVault(rollupID, address(asset), _vault); + + // Rollup should be registered, vault and escrow deployed + assertEq(l1Deployer.getRollupContract(rollupID), rollupContract); + assertEq(l1Deployer.getEscrowManager(rollupID), rollupAdmin); + assertEq(l1Deployer.getEscrow(rollupID, address(asset)), _l1Escrow); + assertEq(l1Deployer.getVault(address(asset)), address(0)); + assertEq(l1Deployer.getVault(address(asset), rollupID), _vault); + + IVault vault = IVault(_vault); + + assertEq(vault.accountant(), address(accountant)); + assertEq(vault.asset(), address(asset)); + ( + address asset_, + uint32 rollupID_, + address _debtAllocator, + uint96 _index + ) = l1Deployer.vaultConfig(_vault); + assertTrue(_debtAllocator != address(0)); + assertEq(_index, 0); + assertEq(rollupID_, rollupID); + assertEq(asset_, address(asset)); + + L1YearnEscrow escrow = L1YearnEscrow(_l1Escrow); + + assertEq(escrow.owner(), rollupAdmin); + assertTrue(escrow.hasRole(escrow.ESCROW_MANAGER_ROLE(), rollupAdmin)); + assertEq(escrow.polygonZkEVMBridge(), address(polygonZkEVMBridge)); + assertEq( + escrow.counterpartContract(), + getL2EscrowAddress(address(asset)) + ); + assertEq(escrow.counterpartNetwork(), rollupID); + assertEq(address(escrow.originTokenAddress()), address(asset)); + assertEq( + address(escrow.wrappedTokenAddress()), + getL2TokenAddress(address(asset)) + ); + assertEq(address(escrow.vaultAddress()), address(vault)); + assertEq(escrow.minimumBuffer(), 0); + assertEq( + asset.allowance(address(escrow), address(vault)), + 2 ** 256 - 1 + ); + } } diff --git a/test/Setup.t.sol b/test/Setup.t.sol index e9d19c3..31992e8 100644 --- a/test/Setup.t.sol +++ b/test/Setup.t.sol @@ -135,19 +135,4 @@ contract SetupTest is Setup { address(l2TokenConverterImpl) ); } - - function test_newVault() public { - // Pretend to be the rollup 1 - uint32 rollupID = 1; - address admin = 0x242daE44F5d8fb54B198D03a94dA45B5a4413e21; - address manager = address(123); - - vm.expectRevert("!admin"); - l1Deployer.registerRollup(rollupID, manager); - - vm.prank(admin); - l1Deployer.registerRollup(rollupID, manager); - - l1Deployer.newEscrow(rollupID, address(asset)); - } } diff --git a/test/utils/Setup.sol b/test/utils/Setup.sol index 62676e6..052431a 100644 --- a/test/utils/Setup.sol +++ b/test/utils/Setup.sol @@ -4,34 +4,34 @@ pragma solidity >=0.8.18; import "forge-std/console.sol"; import {ExtendedTest} from "./ExtendedTest.sol"; -import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {Proxy} from "@zkevm-stb/Proxy.sol"; +import {L2Token} from "@zkevm-stb/L2Token.sol"; +import {L2Escrow} from "@zkevm-stb/L2Escrow.sol"; +import {L2TokenConverter} from "@zkevm-stb/L2TokenConverter.sol"; + +import {L1Deployer} from "../../src/L1Deployer.sol"; +import {L2Deployer} from "../../src/L2Deployer.sol"; +import {L1YearnEscrow} from "../../src/L1YearnEscrow.sol"; import {Roles} from "@yearn-vaults/interfaces/Roles.sol"; import {IVault} from "@yearn-vaults/interfaces/IVault.sol"; import {IStrategy} from "../../src/interfaces/Yearn/IStrategy.sol"; import {IVaultFactory} from "@yearn-vaults/interfaces/IVaultFactory.sol"; -import {Registry, RegistryFactory} from "@vault-periphery/registry/RegistryFactory.sol"; -import {DebtAllocator, DebtAllocatorFactory} from "@vault-periphery/debtAllocators/DebtAllocatorFactory.sol"; +import {MockTokenizedStrategy} from "../mocks/MockTokenizedStrategy.sol"; import {ICREATE3Factory} from "../../src/interfaces/ICREATE3Factory.sol"; -import {IPolygonZkEVMBridge} from "../../src/interfaces/Polygon/IPolygonZkEVMBridge.sol"; +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IAccountant} from "../../src/interfaces/Yearn/IAccountant.sol"; import {IAccountantFactory} from "../../src/interfaces/Yearn/IAccountantFactory.sol"; -import {L1Deployer} from "../../src/L1Deployer.sol"; -import {L2Deployer} from "../../src/L2Deployer.sol"; -import {L1YearnEscrow} from "../../src/L1YearnEscrow.sol"; - -import {Proxy} from "@zkevm-stb/Proxy.sol"; -import {L2Escrow} from "@zkevm-stb/L2Escrow.sol"; -import {L2Token} from "@zkevm-stb/L2Token.sol"; -import {L2TokenConverter} from "@zkevm-stb/L2TokenConverter.sol"; +import {IPolygonZkEVMBridge} from "../../src/interfaces/Polygon/IPolygonZkEVMBridge.sol"; -import {MockTokenizedStrategy} from "../mocks/MockTokenizedStrategy.sol"; +import {Registry, RegistryFactory} from "@vault-periphery/registry/RegistryFactory.sol"; +import {DebtAllocator, DebtAllocatorFactory} from "@vault-periphery/debtAllocators/DebtAllocatorFactory.sol"; contract Setup is ExtendedTest { using SafeERC20 for ERC20; @@ -39,14 +39,12 @@ contract Setup is ExtendedTest { // Contract instances that we will use repeatedly. ERC20 public asset; - IPolygonZkEVMBridge public polygonZkEVMBridge = - IPolygonZkEVMBridge(0x2a3DD3EB832aF982ec71669E178424b10Dca2EDe); - - address public rollupManager = 0x5132A183E9F3CB7C848b0AAC5Ae0c4f0491B7aB2; - ICREATE3Factory internal constant create3Factory = ICREATE3Factory(0x93FEC2C00BfE902F733B57c5a6CeeD7CD1384AE1); + IPolygonZkEVMBridge public polygonZkEVMBridge = + IPolygonZkEVMBridge(0x2a3DD3EB832aF982ec71669E178424b10Dca2EDe); + // Vault contracts to test with. IVault public vault; // Vault Factory v3.0.2 @@ -67,18 +65,18 @@ contract Setup is ExtendedTest { /// Core Contracts \\\\ - ///// L1 Contracts \\\\\ + ///// L1 Contracts \\\\ L1Deployer public l1Deployer; L1YearnEscrow public l1EscrowImpl; //// L2 Contracts \\\\\ - L2Deployer public l2Deployer; + L2Token public l2TokenImpl; L2Escrow public l2EscrowImpl; - L2Token public l2TokenImpl; + L2Deployer public l2Deployer; L2TokenConverter public l2TokenConverterImpl;