From 228a3bebac706b9ba6e335892cc64f751cf1be28 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Mon, 9 Sep 2024 18:37:21 +0000 Subject: [PATCH 1/7] feat: token changes --- .../mainnet/bEIGEN_timelock_reduction.s.sol | 72 ++++++++++++ script/deploy/mainnet/bEIGEN_upgrade.s.sol | 104 ++++++++++++++++++ src/contracts/interfaces/IBackingEigen.sol | 27 +++++ src/contracts/interfaces/IEigen.sol | 6 + src/contracts/token/BackingEigen.sol | 30 ++++- src/contracts/token/Eigen.sol | 13 ++- .../token/EigenTransferRestrictions.t.sol | 14 ++- src/test/token/EigenWrapping.t.sol | 33 ++++-- src/test/token/bEIGEN.t.sol | 62 ++++++++++- 9 files changed, 337 insertions(+), 24 deletions(-) create mode 100644 script/deploy/mainnet/bEIGEN_timelock_reduction.s.sol create mode 100644 script/deploy/mainnet/bEIGEN_upgrade.s.sol diff --git a/script/deploy/mainnet/bEIGEN_timelock_reduction.s.sol b/script/deploy/mainnet/bEIGEN_timelock_reduction.s.sol new file mode 100644 index 000000000..d4d0b4813 --- /dev/null +++ b/script/deploy/mainnet/bEIGEN_timelock_reduction.s.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "@openzeppelin/contracts/governance/TimelockController.sol"; + +import "forge-std/Script.sol"; +import "forge-std/Test.sol"; + +// # To load the variables in the .env file +// source .env + +// # To deploy and verify our contract +// forge script script/deploy/mainnet/bEIGEN_timelock_reduction.s.sol:bEIGEN_timelock_reduction -vvvv --rpc-url $RPC_URL +contract bEIGEN_timelock_reduction is Script, Test { + Vm cheats = Vm(HEVM_ADDRESS); + + TimelockController public bEIGEN_TimelockController = TimelockController(payable(0xd6EC41E453C5E7dA5494f4d51A053Ab571712E6f)); + address public bEIGEN_TimelockAdmin = 0xbb00DDa2832850a43840A3A86515E3Fe226865F2; + + uint256 public newDelay = 0; + + function run() external { + // Read and log the chain ID + uint256 chainId = block.chainid; + emit log_named_uint("You are deploying on ChainID", chainId); + + if (chainId == 1) { + // rpcUrl = "RPC_MAINNET"; + } else { + revert("Chain not supported"); + } + + uint256 minDelayBefore = bEIGEN_TimelockController.getMinDelay(); + + require(minDelayBefore == 24 days, + "something horribly wrong"); + + bytes memory proposalData = abi.encodeWithSelector( + TimelockController.updateDelay.selector, + newDelay + ); + emit log_named_bytes("proposalData", proposalData); + + // propose change to zero delay + vm.startPrank(bEIGEN_TimelockAdmin); + bEIGEN_TimelockController.schedule({ + target: address(bEIGEN_TimelockController), + value: 0, + data: proposalData, + predecessor: bytes32(0), + salt: bytes32(0), + delay: minDelayBefore + }); + + // fast-forward to after current delay and execute + vm.warp(block.timestamp + minDelayBefore); + bEIGEN_TimelockController.execute({ + target: address(bEIGEN_TimelockController), + value: 0, + payload: proposalData, + predecessor: bytes32(0), + salt: bytes32(0) + }); + + cheats.stopPrank(); + + uint256 minDelayAfter = bEIGEN_TimelockController.getMinDelay(); + + require(minDelayAfter == 0, + "min delay not set to zero"); + } +} \ No newline at end of file diff --git a/script/deploy/mainnet/bEIGEN_upgrade.s.sol b/script/deploy/mainnet/bEIGEN_upgrade.s.sol new file mode 100644 index 000000000..b4eabdbc2 --- /dev/null +++ b/script/deploy/mainnet/bEIGEN_upgrade.s.sol @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "@openzeppelin/contracts/governance/TimelockController.sol"; + +import "../../../src/contracts/token/BackingEigen.sol"; + +import "forge-std/Script.sol"; +import "forge-std/Test.sol"; + +// # To load the variables in the .env file +// source .env + +// # To deploy and verify our contract +// forge script script/deploy/mainnet/bEIGEN_upgrade.s.sol:bEIGEN_upgrade -vvvv --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast +contract bEIGEN_upgrade is Script, Test { + Vm cheats = Vm(HEVM_ADDRESS); + + BackingEigen public bEIGEN_proxy = BackingEigen(0x83E9115d334D248Ce39a6f36144aEaB5b3456e75); + BackingEigen public bEIGEN_implementation; + ProxyAdmin public bEIGEN_ProxyAdmin = ProxyAdmin(0x3f5Ab2D4418d38568705bFd6672630fCC3435CC9); + TimelockController public bEIGEN_TimelockController = TimelockController(payable(0xd6EC41E453C5E7dA5494f4d51A053Ab571712E6f)); + address public bEIGEN_TimelockAdmin = 0xbb00DDa2832850a43840A3A86515E3Fe226865F2; + + // // RPC url to fork from for pre-upgrade state change tests + // string public rpcUrl; + + IERC20 public EIGEN_addressBefore; + + function run() external { + // Read and log the chain ID + uint256 chainId = block.chainid; + emit log_named_uint("You are deploying on ChainID", chainId); + + if (chainId == 1) { + // rpcUrl = "RPC_MAINNET"; + } else { + revert("Chain not supported"); + } + + EIGEN_addressBefore = bEIGEN_proxy.EIGEN(); + + require(EIGEN_addressBefore == IERC20(0xec53bF9167f50cDEB3Ae105f56099aaaB9061F83), + "something horribly wrong"); + + // Begin deployment + vm.startBroadcast(); + + // Deploy new implmementation contract + bEIGEN_implementation = new BackingEigen({ + _EIGEN: EIGEN_addressBefore + }); + + vm.stopBroadcast(); + + emit log_named_address("bEIGEN_implementation", address(bEIGEN_implementation)); + + // Perform post-upgrade tests + simulatePerformingUpgrade(); + checkUpgradeCorrectness(); + } + + function simulatePerformingUpgrade() public { + // Upgrade beacon + uint256 delay = bEIGEN_TimelockController.getMinDelay(); + bytes memory data = abi.encodeWithSelector( + ProxyAdmin.upgrade.selector, + TransparentUpgradeableProxy(payable(address(bEIGEN_proxy))), + bEIGEN_implementation + ); + emit log_named_bytes("data", data); + + vm.startPrank(bEIGEN_TimelockAdmin); + bEIGEN_TimelockController.schedule({ + target: address(bEIGEN_ProxyAdmin), + value: 0, + data: data, + predecessor: bytes32(0), + salt: bytes32(0), + delay: delay + }); + + vm.warp(block.timestamp + delay); + bEIGEN_TimelockController.execute({ + target: address(bEIGEN_ProxyAdmin), + value: 0, + payload: data, + predecessor: bytes32(0), + salt: bytes32(0) + }); + + cheats.stopPrank(); + } + + function checkUpgradeCorrectness() public { + vm.prank(address(bEIGEN_TimelockController)); + require(bEIGEN_ProxyAdmin.getProxyImplementation(TransparentUpgradeableProxy(payable(address(bEIGEN_proxy)))) == address(bEIGEN_implementation), + "implementation set incorrectly"); + require(bEIGEN_proxy.EIGEN() == EIGEN_addressBefore, + "EIGEN address changed unexpectedly"); + } +} \ No newline at end of file diff --git a/src/contracts/interfaces/IBackingEigen.sol b/src/contracts/interfaces/IBackingEigen.sol index b50c88ba2..2542d734a 100644 --- a/src/contracts/interfaces/IBackingEigen.sol +++ b/src/contracts/interfaces/IBackingEigen.sol @@ -23,6 +23,33 @@ interface IBackingEigen is IERC20 { */ function disableTransferRestrictions() external; + /** + * @notice An initializer function that sets initial values for the contract's state variables. + */ + function initialize(address initialOwner) external; + + // @notice Allows the contract owner to modify an entry in the `isMinter` mapping. + function setIsMinter(address minterAddress, bool newStatus) external; + + /** + * @notice Allows any privileged address to mint `amount` new tokens to the address `to`. + * @dev Callable only by an address that has `isMinter` set to true. + */ + function mint(address to, uint256 amount) external; + + /** + * @dev Destroys `amount` tokens from the caller. + * + * See {ERC20-_burn}. + */ + function burn(uint256 amount) external; + + /// @notice the address of the wrapped Eigen token EIGEN + function EIGEN() external view returns (IERC20); + + /// @notice the timestamp after which transfer restrictions are disabled + function transferRestrictionsDisabledAfter() external view returns (uint256); + /** * @dev Clock used for flagging checkpoints. Has been overridden to implement timestamp based * checkpoints (and voting). diff --git a/src/contracts/interfaces/IEigen.sol b/src/contracts/interfaces/IEigen.sol index cd9279e55..1809902d4 100644 --- a/src/contracts/interfaces/IEigen.sol +++ b/src/contracts/interfaces/IEigen.sol @@ -38,6 +38,12 @@ interface IEigen is IERC20 { */ function unwrap(uint256 amount) external; + // @notice Burns EIGEN tokens held by the EIGEN token address itself + function burnExtraTokens() external; + + /// @notice the address of the backing Eigen token bEIGEN + function bEIGEN() external view returns (IERC20); + /** * @dev Clock used for flagging checkpoints. Has been overridden to implement timestamp based * checkpoints (and voting). diff --git a/src/contracts/token/BackingEigen.sol b/src/contracts/token/BackingEigen.sol index 0a3b707ec..c8a97160d 100644 --- a/src/contracts/token/BackingEigen.sol +++ b/src/contracts/token/BackingEigen.sol @@ -17,6 +17,8 @@ contract BackingEigen is OwnableUpgradeable, ERC20VotesUpgradeable { mapping(address => bool) public allowedFrom; /// @notice mapping of addresses that are allowed to receive tokens from any address mapping(address => bool) public allowedTo; + // @notice whether or not an address is allowed to mint new bEIGEN tokens + mapping(address => bool) public isMinter; /// @notice event emitted when the allowedFrom status of an address is set event SetAllowedFrom(address indexed from, bool isAllowedFrom); @@ -26,12 +28,38 @@ contract BackingEigen is OwnableUpgradeable, ERC20VotesUpgradeable { event TransferRestrictionsDisabled(); /// @notice event emitted when the EIGEN token is backed event Backed(); + // @notice event emitted when the `isMinter` mapping is modified + event IsMinterModified(address indexed minterAddress, bool newStatus); constructor(IERC20 _EIGEN) { EIGEN = _EIGEN; _disableInitializers(); } + // @notice Allows the contract owner to modify an entry in the `isMinter` mapping. + function setIsMinter(address minterAddress, bool newStatus) external onlyOwner { + emit IsMinterModified(minterAddress, newStatus); + isMinter[minterAddress] = newStatus; + } + + /** + * @notice Allows any privileged address to mint `amount` new tokens to the address `to`. + * @dev Callable only by an address that has `isMinter` set to true. + */ + function mint(address to, uint256 amount) external { + require(isMinter[msg.sender], "BackingEigen.mint: caller is not a minter"); + _mint(to, amount); + } + + /** + * @dev Destroys `amount` tokens from the caller. + * + * See {ERC20-_burn}. + */ + function burn(uint256 amount) public virtual { + _burn(_msgSender(), amount); + } + /** * @notice An initializer function that sets initial values for the contract's state variables. */ @@ -51,7 +79,7 @@ contract BackingEigen is OwnableUpgradeable, ERC20VotesUpgradeable { // Mint the entire supply of EIGEN - this is a one-time event that // ensures bEIGEN fully backs EIGEN. - _mint(address(EIGEN), EIGEN.totalSupply()); + _mint(address(EIGEN), 1673646668284660000000000000); emit Backed(); } diff --git a/src/contracts/token/Eigen.sol b/src/contracts/token/Eigen.sol index 327fe13b4..1d3cc3774 100644 --- a/src/contracts/token/Eigen.sol +++ b/src/contracts/token/Eigen.sol @@ -123,14 +123,14 @@ contract Eigen is OwnableUpgradeable, ERC20VotesUpgradeable { */ function wrap(uint256 amount) external { require(bEIGEN.transferFrom(msg.sender, address(this), amount), "Eigen.wrap: bEIGEN transfer failed"); - _transfer(address(this), msg.sender, amount); + _mint(msg.sender, amount); } /** * @notice This function allows Eigen holders to unwrap their tokens into bEIGEN */ function unwrap(uint256 amount) external { - _transfer(msg.sender, address(this), amount); + _burn(msg.sender, amount); require(bEIGEN.transfer(msg.sender, amount), "Eigen.unwrap: bEIGEN transfer failed"); } @@ -162,6 +162,15 @@ contract Eigen is OwnableUpgradeable, ERC20VotesUpgradeable { super._beforeTokenTransfer(from, to, amount); } + /** + * @notice Overridden to return the total bEIGEN supply instead. + * @dev The issued supply of EIGEN should match the bEIGEN balance of this contract, + * less any bEIGEN tokens that were sent directly to the contract (rather than being wrapped) + */ + function totalSupply() public view override returns (uint256) { + return bEIGEN.totalSupply(); + } + /** * @dev Clock used for flagging checkpoints. Has been overridden to implement timestamp based * checkpoints (and voting). diff --git a/src/test/token/EigenTransferRestrictions.t.sol b/src/test/token/EigenTransferRestrictions.t.sol index 37b690212..c254f97a7 100644 --- a/src/test/token/EigenTransferRestrictions.t.sol +++ b/src/test/token/EigenTransferRestrictions.t.sol @@ -3,8 +3,9 @@ pragma solidity ^0.8.12; import "forge-std/Test.sol"; -import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "@openzeppelin-v4.9.0/contracts/proxy/transparent/ProxyAdmin.sol"; +import "@openzeppelin-v4.9.0/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "@openzeppelin-v4.9.0/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; import "../harnesses/EigenHarness.sol"; contract EigenTransferRestrictionsTest is Test { @@ -38,8 +39,15 @@ contract EigenTransferRestrictionsTest is Test { vm.startPrank(minter1); proxyAdmin = new ProxyAdmin(); // initialize with dummy BackingEigen address - eigenImpl = new EigenHarness(IERC20(address(0))); + + eigenImpl = new EigenHarness(new ERC20PresetFixedSupply({ + name: "bEIGEN", + symbol: "bEIGEN", + initialSupply: totalSupply, + owner: minter1 + })); eigen = Eigen(address(new TransparentUpgradeableProxy(address(eigenImpl), address(proxyAdmin), ""))); + eigen.bEIGEN().transfer(address(eigen), totalSupply); vm.stopPrank(); fuzzedOutAddresses[minter1] = true; diff --git a/src/test/token/EigenWrapping.t.sol b/src/test/token/EigenWrapping.t.sol index d2a8683aa..862beb317 100644 --- a/src/test/token/EigenWrapping.t.sol +++ b/src/test/token/EigenWrapping.t.sol @@ -72,10 +72,6 @@ contract EigenWrappingTests is Test { _simulateMint(); _simulateBackingAndSetTransferRestrictions(); - // initial bEIGEN balance - uint256 initialBEIGENBalanceOfEigenToken = bEIGEN.balanceOf(address(eigen)); - // initial EIGEN token supply - uint256 initialEigenSupply = eigen.totalSupply(); // minter1 balance uint256 minter1Balance = eigen.balanceOf(minter1); @@ -83,15 +79,23 @@ contract EigenWrappingTests is Test { vm.prank(minter1); eigen.transfer(unwrapper, minter1Balance); + // initial bEIGEN balance + uint256 initialBEIGENBalanceOfEigenToken = bEIGEN.balanceOf(address(eigen)); + // initial EIGEN token supply + assertEq(eigen.totalSupply(), bEIGEN.totalSupply(), + "eigen totalSupply changed incorrectly"); + // unwrap // unwrap amount should be less than minter1 balance unwrapAmount = unwrapAmount % minter1Balance; vm.prank(unwrapper); eigen.unwrap(unwrapAmount); - // check that the total supply of bEIGEN is equal to the total supply of EIGEN - assertEq(eigen.totalSupply(), initialEigenSupply); - assertEq(bEIGEN.balanceOf(address(eigen)), initialBEIGENBalanceOfEigenToken - unwrapAmount); + // check total supply and balance changes + assertEq(eigen.totalSupply(), bEIGEN.totalSupply(), + "eigen totalSupply changed incorrectly"); + assertEq(bEIGEN.balanceOf(address(eigen)), initialBEIGENBalanceOfEigenToken - unwrapAmount, + "beigen balance of EIGEN tokens changed incorrectly"); assertEq(eigen.balanceOf(address(unwrapper)), minter1Balance - unwrapAmount); assertEq(bEIGEN.balanceOf(address(unwrapper)), unwrapAmount); } @@ -104,8 +108,6 @@ contract EigenWrappingTests is Test { // initial bEIGEN balance uint256 initialBEIGENBalanceOfEigenToken = bEIGEN.balanceOf(address(eigen)); - // initial EIGEN token supply - uint256 initialEigenSupply = eigen.totalSupply(); // minter1 balance uint256 minter1Balance = eigen.balanceOf(minter1); @@ -117,6 +119,10 @@ contract EigenWrappingTests is Test { bEIGEN.transfer(wrapper, minter1Balance); vm.stopPrank(); + // initial EIGEN token supply + assertEq(eigen.totalSupply(), bEIGEN.totalSupply(), + "eigen totalSupply changed incorrectly"); + // wrap // wrap amount should be less than minter1 balance wrapAmount = wrapAmount % minter1Balance; @@ -127,8 +133,9 @@ contract EigenWrappingTests is Test { eigen.wrap(wrapAmount); vm.stopPrank(); - // check that the total supply of bEIGEN is equal to the total supply of EIGEN - assertEq(eigen.totalSupply(), initialEigenSupply); + // check total supply and balance changes + assertEq(eigen.totalSupply(), bEIGEN.totalSupply(), + "eigen totalSupply changed incorrectly"); assertEq(bEIGEN.balanceOf(address(eigen)), initialBEIGENBalanceOfEigenToken - minter1Balance + wrapAmount); assertEq(eigen.balanceOf(address(wrapper)), wrapAmount); assertEq(bEIGEN.balanceOf(address(wrapper)), minter1Balance - wrapAmount); @@ -147,7 +154,7 @@ contract EigenWrappingTests is Test { // unwrap vm.prank(unwrapper); - vm.expectRevert("ERC20: transfer amount exceeds balance"); + vm.expectRevert("ERC20: burn amount exceeds balance"); eigen.unwrap(unwrapAmount + 1); } @@ -195,6 +202,8 @@ contract EigenWrappingTests is Test { vm.startPrank(minter1); bEIGEN.setAllowedFrom(minter1, true); + eigen.setAllowedTo(address(0), true); vm.stopPrank(); + } } diff --git a/src/test/token/bEIGEN.t.sol b/src/test/token/bEIGEN.t.sol index 5338c5702..06de8b60e 100644 --- a/src/test/token/bEIGEN.t.sol +++ b/src/test/token/bEIGEN.t.sol @@ -13,7 +13,9 @@ import "../../contracts/token/BackingEigen.sol"; contract bEIGENTest is Test { mapping(address => bool) fuzzedOutAddresses; - address minter1 = 0xbb00DDa2832850a43840A3A86515E3Fe226865F2; + address initialOwner = 0xbb00DDa2832850a43840A3A86515E3Fe226865F2; + address minterToSet = address(500); + address mintTo = address(12345); ProxyAdmin proxyAdmin; @@ -25,7 +27,7 @@ contract bEIGENTest is Test { function setUp() public { - vm.startPrank(minter1); + vm.startPrank(initialOwner); proxyAdmin = new ProxyAdmin(); // deploy proxies @@ -44,10 +46,10 @@ contract bEIGENTest is Test { } function test_Initialize() public { - bEIGEN.initialize(minter1); + bEIGEN.initialize(initialOwner); - // check that the owner is minter1 - assertEq(bEIGEN.owner(), minter1); + // check that the owner is initialOwner + assertEq(bEIGEN.owner(), initialOwner); // check the transfer restrictions are disabled after one year in the future assertEq(bEIGEN.transferRestrictionsDisabledAfter(), type(uint256).max); } @@ -55,10 +57,58 @@ contract bEIGENTest is Test { function testFuzz_CanBackTheEigenToken(uint eigenSupply) public { StdCheats.deal(address(eigen), address(this), eigenSupply); - bEIGEN.initialize(minter1); + bEIGEN.initialize(initialOwner); // check that the total supply of bEIGEN is equal to the total supply of EIGEN assertEq(bEIGEN.totalSupply(), eigen.totalSupply()); assertEq(bEIGEN.balanceOf(address(eigen)), bEIGEN.totalSupply()); } + + function test_setIsMinterAndMint() public { + bEIGEN.initialize(initialOwner); + + vm.prank(initialOwner); + bEIGEN.setIsMinter(minterToSet, true); + require(bEIGEN.isMinter(minterToSet), "minter not set correctly"); + + uint256 amountToMint = 5e25; + uint256 balanceBefore = bEIGEN.balanceOf(mintTo); + vm.prank(minterToSet); + bEIGEN.mint(mintTo, amountToMint); + + uint256 balanceAfter = bEIGEN.balanceOf(mintTo); + uint256 balanceDiff = balanceAfter - balanceBefore; + assertEq(balanceDiff, amountToMint, "mint not working correctly"); + } + + function test_setIsMinter_revertsWhenNotCalledByOwner() public { + bEIGEN.initialize(initialOwner); + + vm.prank(mintTo); + vm.expectRevert("Ownable: caller is not the owner"); + bEIGEN.setIsMinter(minterToSet, true); + } + + function test_burn() public { + test_setIsMinterAndMint(); + vm.prank(initialOwner); + bEIGEN.setAllowedFrom(mintTo, true); + + uint256 amountToBurn = 1005e18; + uint256 balanceBefore = bEIGEN.balanceOf(mintTo); + vm.prank(mintTo); + bEIGEN.burn(amountToBurn); + + uint256 balanceAfter = bEIGEN.balanceOf(mintTo); + uint256 balanceDiff = balanceBefore - balanceAfter; + assertEq(balanceDiff, amountToBurn, "mint not working correctly"); + } + + function test_mint_revertsWhenNotCalledByMinter() public { + test_setIsMinterAndMint(); + + uint256 amountToMint = 5e25; + vm.expectRevert("BackingEigen.mint: caller is not a minter"); + bEIGEN.mint(mintTo, amountToMint); + } } From f6e49aca4d76740b44a97a78ae398783a54be001 Mon Sep 17 00:00:00 2001 From: MinisculeTarantula Date: Mon, 9 Sep 2024 13:08:20 -0700 Subject: [PATCH 2/7] fix: make transfer restrictions work with wrap/unwrap changes switch `address(this)` => `address(0)` to allow wrapping + unwrapping while transfer restrictions are in place this aligns transfer restrictions with the modified wrap/unwrap behavior (where tokens are minted/burned instead of transferred from the token's own address) --- src/contracts/token/Eigen.sol | 2 +- src/test/token/EigenWrapping.t.sol | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/contracts/token/Eigen.sol b/src/contracts/token/Eigen.sol index 1d3cc3774..fcd15cf15 100644 --- a/src/contracts/token/Eigen.sol +++ b/src/contracts/token/Eigen.sol @@ -155,7 +155,7 @@ contract Eigen is OwnableUpgradeable, ERC20VotesUpgradeable { if (block.timestamp <= transferRestrictionsDisabledAfter) { // if both from and to are not whitelisted require( - from == address(0) || from == address(this) || to == address(this) || allowedFrom[from] || allowedTo[to], + from == address(0) || to == address(0) || allowedFrom[from] || allowedTo[to], "Eigen._beforeTokenTransfer: from or to must be whitelisted" ); } diff --git a/src/test/token/EigenWrapping.t.sol b/src/test/token/EigenWrapping.t.sol index 862beb317..76b9e6385 100644 --- a/src/test/token/EigenWrapping.t.sol +++ b/src/test/token/EigenWrapping.t.sol @@ -202,7 +202,6 @@ contract EigenWrappingTests is Test { vm.startPrank(minter1); bEIGEN.setAllowedFrom(minter1, true); - eigen.setAllowedTo(address(0), true); vm.stopPrank(); } From 8498c68ec1df69642248e2586e8303d8911e53d9 Mon Sep 17 00:00:00 2001 From: MinisculeTarantula Date: Tue, 10 Sep 2024 09:06:46 -0700 Subject: [PATCH 3/7] chore: add minimal scripts for EIGEN changes --- .../mainnet/EIGEN_timelock_reduction.s.sol | 72 ++++++++++ script/deploy/mainnet/EIGEN_upgrade.s.sol | 130 ++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 script/deploy/mainnet/EIGEN_timelock_reduction.s.sol create mode 100644 script/deploy/mainnet/EIGEN_upgrade.s.sol diff --git a/script/deploy/mainnet/EIGEN_timelock_reduction.s.sol b/script/deploy/mainnet/EIGEN_timelock_reduction.s.sol new file mode 100644 index 000000000..279d23e5c --- /dev/null +++ b/script/deploy/mainnet/EIGEN_timelock_reduction.s.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "@openzeppelin/contracts/governance/TimelockController.sol"; + +import "forge-std/Script.sol"; +import "forge-std/Test.sol"; + +// # To load the variables in the .env file +// source .env + +// # To deploy and verify our contract +// forge script script/deploy/mainnet/EIGEN_timelock_reduction.s.sol:EIGEN_timelock_reduction -vvvv --rpc-url $RPC_URL +contract EIGEN_timelock_reduction is Script, Test { + Vm cheats = Vm(HEVM_ADDRESS); + + TimelockController public EIGEN_TimelockController = TimelockController(payable(0x2520C6b2C1FBE1813AB5c7c1018CDa39529e9FF2)); + address public EIGEN_TimelockAdmin = 0xbb00DDa2832850a43840A3A86515E3Fe226865F2; + + uint256 public newDelay = 0; + + function run() external { + // Read and log the chain ID + uint256 chainId = block.chainid; + emit log_named_uint("You are deploying on ChainID", chainId); + + if (chainId == 1) { + // rpcUrl = "RPC_MAINNET"; + } else { + revert("Chain not supported"); + } + + uint256 minDelayBefore = EIGEN_TimelockController.getMinDelay(); + + require(minDelayBefore == 10 days, + "something horribly wrong"); + + bytes memory proposalData = abi.encodeWithSelector( + TimelockController.updateDelay.selector, + newDelay + ); + emit log_named_bytes("proposalData", proposalData); + + // propose change to zero delay + vm.startPrank(EIGEN_TimelockAdmin); + EIGEN_TimelockController.schedule({ + target: address(EIGEN_TimelockController), + value: 0, + data: proposalData, + predecessor: bytes32(0), + salt: bytes32(0), + delay: minDelayBefore + }); + + // fast-forward to after current delay and execute + vm.warp(block.timestamp + minDelayBefore); + EIGEN_TimelockController.execute({ + target: address(EIGEN_TimelockController), + value: 0, + payload: proposalData, + predecessor: bytes32(0), + salt: bytes32(0) + }); + + cheats.stopPrank(); + + uint256 minDelayAfter = EIGEN_TimelockController.getMinDelay(); + + require(minDelayAfter == 0, + "min delay not set to zero"); + } +} \ No newline at end of file diff --git a/script/deploy/mainnet/EIGEN_upgrade.s.sol b/script/deploy/mainnet/EIGEN_upgrade.s.sol new file mode 100644 index 000000000..b2f6514cf --- /dev/null +++ b/script/deploy/mainnet/EIGEN_upgrade.s.sol @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "@openzeppelin/contracts/governance/TimelockController.sol"; + +import "../../../src/contracts/token/BackingEigen.sol"; +import "../../../src/contracts/token/Eigen.sol"; + +import "forge-std/Script.sol"; +import "forge-std/Test.sol"; + +// # To load the variables in the .env file +// source .env + +// # To deploy and verify our contract +// forge script script/deploy/mainnet/EIGEN_upgrade.s.sol:EIGEN_upgrade -vvvv --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast +contract EIGEN_upgrade is Script, Test { + Vm cheats = Vm(HEVM_ADDRESS); + + BackingEigen public bEIGEN_proxy = BackingEigen(0x83E9115d334D248Ce39a6f36144aEaB5b3456e75); + BackingEigen public bEIGEN_implementation; + Eigen public EIGEN_proxy = Eigen(0xec53bF9167f50cDEB3Ae105f56099aaaB9061F83); + Eigen public EIGEN_implementation; + ProxyAdmin public EIGEN_ProxyAdmin = ProxyAdmin(0xB8915E195121f2B5D989Ec5727fd47a5259F1CEC); + TimelockController public EIGEN_TimelockController = TimelockController(payable(0x2520C6b2C1FBE1813AB5c7c1018CDa39529e9FF2)); + address public EIGEN_TimelockAdmin = 0xbb00DDa2832850a43840A3A86515E3Fe226865F2; + + // // RPC url to fork from for pre-upgrade state change tests + // string public rpcUrl; + + IERC20 public bEIGEN_addressBefore; + + function run() external { + // Read and log the chain ID + uint256 chainId = block.chainid; + emit log_named_uint("You are deploying on ChainID", chainId); + + if (chainId == 1) { + // rpcUrl = "RPC_MAINNET"; + } else { + revert("Chain not supported"); + } + + bEIGEN_addressBefore = EIGEN_proxy.bEIGEN(); + + require(bEIGEN_addressBefore == IERC20(0x83E9115d334D248Ce39a6f36144aEaB5b3456e75), + "something horribly wrong"); + + // Begin deployment + vm.startBroadcast(); + + // Deploy new implmementation contract + EIGEN_implementation = new Eigen({ + _bEIGEN: bEIGEN_addressBefore + }); + + vm.stopBroadcast(); + + emit log_named_address("EIGEN_implementation", address(EIGEN_implementation)); + + // Perform post-upgrade tests + simulatePerformingUpgrade(); + checkUpgradeCorrectness(); + simulateWrapAndUnwrap(); + } + + function simulatePerformingUpgrade() public { + // Upgrade beacon + uint256 delay = EIGEN_TimelockController.getMinDelay(); + bytes memory data = abi.encodeWithSelector( + ProxyAdmin.upgrade.selector, + TransparentUpgradeableProxy(payable(address(EIGEN_proxy))), + EIGEN_implementation + ); + emit log_named_bytes("data", data); + + vm.startPrank(EIGEN_TimelockAdmin); + EIGEN_TimelockController.schedule({ + target: address(EIGEN_ProxyAdmin), + value: 0, + data: data, + predecessor: bytes32(0), + salt: bytes32(0), + delay: delay + }); + + vm.warp(block.timestamp + delay); + EIGEN_TimelockController.execute({ + target: address(EIGEN_ProxyAdmin), + value: 0, + payload: data, + predecessor: bytes32(0), + salt: bytes32(0) + }); + + cheats.stopPrank(); + } + + function checkUpgradeCorrectness() public { + vm.prank(address(EIGEN_TimelockController)); + require(EIGEN_ProxyAdmin.getProxyImplementation(TransparentUpgradeableProxy(payable(address(EIGEN_proxy)))) == address(EIGEN_implementation), + "implementation set incorrectly"); + require(EIGEN_proxy.bEIGEN() == bEIGEN_addressBefore, + "bEIGEN address changed unexpectedly"); + } + + function simulateWrapAndUnwrap() public { + uint256 amount = 1e18; + cheats.prank(address(EIGEN_proxy)); + bEIGEN_proxy.transfer(address(this), amount); + + bEIGEN_proxy.approve(address(EIGEN_proxy), amount); + uint256 bEIGEN_balanceStart = bEIGEN_proxy.balanceOf(address(this)); + uint256 EIGEN_balanceStart = EIGEN_proxy.balanceOf(address(this)); + EIGEN_proxy.wrap(amount); + uint256 bEIGEN_balanceMiddle = bEIGEN_proxy.balanceOf(address(this)); + uint256 EIGEN_balanceMiddle = EIGEN_proxy.balanceOf(address(this)); + EIGEN_proxy.unwrap(amount); + uint256 bEIGEN_balanceAfter = bEIGEN_proxy.balanceOf(address(this)); + uint256 EIGEN_balanceAfter = EIGEN_proxy.balanceOf(address(this)); + + require(bEIGEN_balanceMiddle + amount == bEIGEN_balanceStart, "wrapping did not transfer out bEIGEN"); + require(EIGEN_balanceMiddle == EIGEN_balanceStart + amount, "wrapping did not transfer in EIGEN"); + + require(bEIGEN_balanceAfter == bEIGEN_balanceStart, "unwrapping did not transfer in bEIGEN"); + require(EIGEN_balanceAfter == EIGEN_balanceStart, "unwrapping did not transfer out EIGEN"); + } +} \ No newline at end of file From f12caf369991f254c978352f97c4c93adf44fd00 Mon Sep 17 00:00:00 2001 From: MinisculeTarantula Date: Tue, 10 Sep 2024 09:41:28 -0700 Subject: [PATCH 4/7] chore: add preprod script --- .../Preprod_Upgrade_bEIGEN_and_EIGEN.s.sol | 112 ++++++++++++++++++ script/deploy/mainnet/EIGEN_upgrade.s.sol | 3 - 2 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 script/deploy/holesky/Preprod_Upgrade_bEIGEN_and_EIGEN.s.sol diff --git a/script/deploy/holesky/Preprod_Upgrade_bEIGEN_and_EIGEN.s.sol b/script/deploy/holesky/Preprod_Upgrade_bEIGEN_and_EIGEN.s.sol new file mode 100644 index 000000000..b8d7484a9 --- /dev/null +++ b/script/deploy/holesky/Preprod_Upgrade_bEIGEN_and_EIGEN.s.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "@openzeppelin/contracts/governance/TimelockController.sol"; + +import "forge-std/Script.sol"; +import "forge-std/Test.sol"; + +import "../../../src/contracts/token/BackingEigen.sol"; +import "../../../src/contracts/token/Eigen.sol"; + +// forge script script/deploy/holesky/Preprod_Upgrade_bEIGEN_and_EIGEN.s.sol --rpc-url $RPC_HOLESKY --private-key $PRIVATE_KEY --broadcast -vvvv --verify --etherscan-api-key $ETHERSCAN_API_KEY +contract Preprod_Upgrade_bEIGEN_and_EIGEN is Script, Test { + Vm cheats = Vm(HEVM_ADDRESS); + + BackingEigen public bEIGEN_proxy = BackingEigen(0xA72942289a043874249E60469F68f08B8c6ECCe8); + BackingEigen public bEIGEN_implementation; + Eigen public EIGEN_proxy = Eigen(0xD58f6844f79eB1fbd9f7091d05f7cb30d3363926); + Eigen public EIGEN_implementation; + ProxyAdmin public EIGEN_ProxyAdmin = ProxyAdmin(0x1BEF05C7303d44e0E2FCD2A19d993eDEd4c51b5B); + address public proxyAdminOwner = 0xDA29BB71669f46F2a779b4b62f03644A84eE3479; + + IERC20 public bEIGEN_addressBefore; + IERC20 public EIGEN_addressBefore; + + function run() external { + // Read and log the chain ID + uint256 chainId = block.chainid; + emit log_named_uint("You are deploying on ChainID", chainId); + + if (chainId != 17000) { + revert("Chain not supported"); + } + + bEIGEN_addressBefore = EIGEN_proxy.bEIGEN(); + require(bEIGEN_addressBefore == IERC20(0xA72942289a043874249E60469F68f08B8c6ECCe8), + "something horribly wrong"); + + EIGEN_addressBefore = bEIGEN_proxy.EIGEN(); + require(EIGEN_addressBefore == IERC20(0xD58f6844f79eB1fbd9f7091d05f7cb30d3363926), + "something horribly wrong"); + + // Begin deployment + vm.startBroadcast(); + + // Deploy new implmementation contracts + EIGEN_implementation = new Eigen({ + _bEIGEN: bEIGEN_addressBefore + }); + bEIGEN_implementation = new BackingEigen({ + _EIGEN: EIGEN_addressBefore + }); + + vm.stopBroadcast(); + + emit log_named_address("EIGEN_implementation", address(EIGEN_implementation)); + emit log_named_address("bEIGEN_implementation", address(bEIGEN_implementation)); + + // Perform upgrade + vm.startBroadcast(); + EIGEN_ProxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(bEIGEN_proxy))), + address(bEIGEN_implementation) + ); + EIGEN_ProxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(EIGEN_proxy))), + address(EIGEN_implementation) + ); + vm.stopBroadcast(); + + // Perform post-upgrade tests + checkUpgradeCorrectness(); + simulateWrapAndUnwrap(); + } + + function checkUpgradeCorrectness() public { + cheats.startPrank(address(proxyAdminOwner)); + require(EIGEN_ProxyAdmin.getProxyImplementation(TransparentUpgradeableProxy(payable(address(EIGEN_proxy)))) == address(EIGEN_implementation), + "implementation set incorrectly"); + require(EIGEN_proxy.bEIGEN() == bEIGEN_addressBefore, + "bEIGEN address changed unexpectedly"); + require(EIGEN_ProxyAdmin.getProxyImplementation(TransparentUpgradeableProxy(payable(address(bEIGEN_proxy)))) == address(bEIGEN_implementation), + "implementation set incorrectly"); + require(bEIGEN_proxy.EIGEN() == EIGEN_addressBefore, + "EIGEN address changed unexpectedly"); + cheats.stopPrank(); + } + + function simulateWrapAndUnwrap() public { + uint256 amount = 1e18; + cheats.prank(address(EIGEN_proxy)); + bEIGEN_proxy.transfer(address(this), amount); + + bEIGEN_proxy.approve(address(EIGEN_proxy), amount); + uint256 bEIGEN_balanceStart = bEIGEN_proxy.balanceOf(address(this)); + uint256 EIGEN_balanceStart = EIGEN_proxy.balanceOf(address(this)); + EIGEN_proxy.wrap(amount); + uint256 bEIGEN_balanceMiddle = bEIGEN_proxy.balanceOf(address(this)); + uint256 EIGEN_balanceMiddle = EIGEN_proxy.balanceOf(address(this)); + EIGEN_proxy.unwrap(amount); + uint256 bEIGEN_balanceAfter = bEIGEN_proxy.balanceOf(address(this)); + uint256 EIGEN_balanceAfter = EIGEN_proxy.balanceOf(address(this)); + + require(bEIGEN_balanceMiddle + amount == bEIGEN_balanceStart, "wrapping did not transfer out bEIGEN"); + require(EIGEN_balanceMiddle == EIGEN_balanceStart + amount, "wrapping did not transfer in EIGEN"); + + require(bEIGEN_balanceAfter == bEIGEN_balanceStart, "unwrapping did not transfer in bEIGEN"); + require(EIGEN_balanceAfter == EIGEN_balanceStart, "unwrapping did not transfer out EIGEN"); + } +} diff --git a/script/deploy/mainnet/EIGEN_upgrade.s.sol b/script/deploy/mainnet/EIGEN_upgrade.s.sol index b2f6514cf..7c1adabf7 100644 --- a/script/deploy/mainnet/EIGEN_upgrade.s.sol +++ b/script/deploy/mainnet/EIGEN_upgrade.s.sol @@ -27,9 +27,6 @@ contract EIGEN_upgrade is Script, Test { TimelockController public EIGEN_TimelockController = TimelockController(payable(0x2520C6b2C1FBE1813AB5c7c1018CDa39529e9FF2)); address public EIGEN_TimelockAdmin = 0xbb00DDa2832850a43840A3A86515E3Fe226865F2; - // // RPC url to fork from for pre-upgrade state change tests - // string public rpcUrl; - IERC20 public bEIGEN_addressBefore; function run() external { From 9f2140700963ee86c7540ea125b54ed89f70eef8 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Mon, 9 Sep 2024 18:37:21 +0000 Subject: [PATCH 5/7] feat: token changes --- src/test/token/EigenWrapping.t.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/token/EigenWrapping.t.sol b/src/test/token/EigenWrapping.t.sol index 76b9e6385..862beb317 100644 --- a/src/test/token/EigenWrapping.t.sol +++ b/src/test/token/EigenWrapping.t.sol @@ -202,6 +202,7 @@ contract EigenWrappingTests is Test { vm.startPrank(minter1); bEIGEN.setAllowedFrom(minter1, true); + eigen.setAllowedTo(address(0), true); vm.stopPrank(); } From 9557a08da1bdd9f2eb0e6d95ad635f331b586a2b Mon Sep 17 00:00:00 2001 From: MinisculeTarantula Date: Mon, 9 Sep 2024 13:08:20 -0700 Subject: [PATCH 6/7] fix: make transfer restrictions work with wrap/unwrap changes switch `address(this)` => `address(0)` to allow wrapping + unwrapping while transfer restrictions are in place this aligns transfer restrictions with the modified wrap/unwrap behavior (where tokens are minted/burned instead of transferred from the token's own address) --- src/test/token/EigenWrapping.t.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/token/EigenWrapping.t.sol b/src/test/token/EigenWrapping.t.sol index 862beb317..76b9e6385 100644 --- a/src/test/token/EigenWrapping.t.sol +++ b/src/test/token/EigenWrapping.t.sol @@ -202,7 +202,6 @@ contract EigenWrappingTests is Test { vm.startPrank(minter1); bEIGEN.setAllowedFrom(minter1, true); - eigen.setAllowedTo(address(0), true); vm.stopPrank(); } From c5d6a9d5d6749f7a8f3ba5da26f8b813fdcfba60 Mon Sep 17 00:00:00 2001 From: MinisculeTarantula Date: Wed, 11 Sep 2024 16:55:49 -0700 Subject: [PATCH 7/7] chore: create testnet upgrade script --- .../holesky/bEIGEN_and_EIGEN_upgrade.s.sol | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 script/deploy/holesky/bEIGEN_and_EIGEN_upgrade.s.sol diff --git a/script/deploy/holesky/bEIGEN_and_EIGEN_upgrade.s.sol b/script/deploy/holesky/bEIGEN_and_EIGEN_upgrade.s.sol new file mode 100644 index 000000000..f63c8e25c --- /dev/null +++ b/script/deploy/holesky/bEIGEN_and_EIGEN_upgrade.s.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "@openzeppelin/contracts/governance/TimelockController.sol"; + +import "../../../src/contracts/token/BackingEigen.sol"; +import "../../../src/contracts/token/Eigen.sol"; + +import "forge-std/Script.sol"; +import "forge-std/Test.sol"; + +// # To load the variables in the .env file +// source .env + +// # To deploy and verify our contract +// forge script script/deploy/holesky/bEIGEN_and_EIGEN_upgrade.s.sol:bEIGEN_and_EIGEN_upgrade -vvv --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast +contract bEIGEN_and_EIGEN_upgrade is Script, Test { + Vm cheats = Vm(HEVM_ADDRESS); + + BackingEigen public bEIGEN_proxy = BackingEigen(0x275cCf9Be51f4a6C94aBa6114cdf2a4c45B9cb27); + BackingEigen public bEIGEN_implementation; + Eigen public EIGEN_proxy = Eigen(0x3B78576F7D6837500bA3De27A60c7f594934027E); + Eigen public EIGEN_implementation; + ProxyAdmin public token_ProxyAdmin = ProxyAdmin(0x67482666771e82C9a73BB9e9A22B2B597f448BBf); + address public opsMultisig = 0xfaEF7338b7490b9E272d80A1a39f4657cAf2b97d; + + IERC20 public bEIGEN_addressBefore; + IERC20 public EIGEN_addressBefore; + + function run() external { + // Read and log the chain ID + uint256 chainId = block.chainid; + emit log_named_uint("You are deploying on ChainID", chainId); + + if (chainId != 17000) { + revert("Chain not supported"); + } + + bEIGEN_addressBefore = EIGEN_proxy.bEIGEN(); + EIGEN_addressBefore = bEIGEN_proxy.EIGEN(); + + require(bEIGEN_addressBefore == IERC20(0x275cCf9Be51f4a6C94aBa6114cdf2a4c45B9cb27), + "something horribly wrong"); + require(EIGEN_addressBefore == IERC20(0x3B78576F7D6837500bA3De27A60c7f594934027E), + "something horribly wrong"); + + // Begin deployment + vm.startBroadcast(); + + // Deploy new implementation contracts + EIGEN_implementation = new Eigen({ + _bEIGEN: bEIGEN_addressBefore + }); + bEIGEN_implementation = new BackingEigen({ + _EIGEN: EIGEN_addressBefore + }); + vm.stopBroadcast(); + + emit log_named_address("EIGEN_implementation", address(EIGEN_implementation)); + emit log_named_address("bEIGEN_implementation", address(bEIGEN_implementation)); + + // Perform post-upgrade tests + simulatePerformingUpgrade(); + checkUpgradeCorrectness(); + simulateWrapAndUnwrap(); + } + + function simulatePerformingUpgrade() public { + cheats.startPrank(opsMultisig); + // Upgrade contracts + token_ProxyAdmin.upgrade(TransparentUpgradeableProxy(payable(address(EIGEN_proxy))), address(EIGEN_implementation)); + token_ProxyAdmin.upgrade(TransparentUpgradeableProxy(payable(address(bEIGEN_proxy))), address(bEIGEN_implementation)); + cheats.stopPrank(); + } + + function checkUpgradeCorrectness() public { + vm.startPrank(opsMultisig); + require(token_ProxyAdmin.getProxyImplementation(TransparentUpgradeableProxy(payable(address(EIGEN_proxy)))) == address(EIGEN_implementation), + "implementation set incorrectly"); + require(EIGEN_proxy.bEIGEN() == bEIGEN_addressBefore, + "bEIGEN address changed unexpectedly"); + require(token_ProxyAdmin.getProxyImplementation(TransparentUpgradeableProxy(payable(address(bEIGEN_proxy)))) == address(bEIGEN_implementation), + "implementation set incorrectly"); + require(bEIGEN_proxy.EIGEN() == EIGEN_addressBefore, + "EIGEN address changed unexpectedly"); + cheats.stopPrank(); + } + + function simulateWrapAndUnwrap() public { + uint256 amount = 1e18; + cheats.prank(address(EIGEN_proxy)); + bEIGEN_proxy.transfer(address(this), amount); + + bEIGEN_proxy.approve(address(EIGEN_proxy), amount); + uint256 bEIGEN_balanceStart = bEIGEN_proxy.balanceOf(address(this)); + uint256 EIGEN_balanceStart = EIGEN_proxy.balanceOf(address(this)); + EIGEN_proxy.wrap(amount); + uint256 bEIGEN_balanceMiddle = bEIGEN_proxy.balanceOf(address(this)); + uint256 EIGEN_balanceMiddle = EIGEN_proxy.balanceOf(address(this)); + EIGEN_proxy.unwrap(amount); + uint256 bEIGEN_balanceAfter = bEIGEN_proxy.balanceOf(address(this)); + uint256 EIGEN_balanceAfter = EIGEN_proxy.balanceOf(address(this)); + + require(bEIGEN_balanceMiddle + amount == bEIGEN_balanceStart, "wrapping did not transfer out bEIGEN"); + require(EIGEN_balanceMiddle == EIGEN_balanceStart + amount, "wrapping did not transfer in EIGEN"); + + require(bEIGEN_balanceAfter == bEIGEN_balanceStart, "unwrapping did not transfer in bEIGEN"); + require(EIGEN_balanceAfter == EIGEN_balanceStart, "unwrapping did not transfer out EIGEN"); + } +} \ No newline at end of file