The home for Hyperlane core contracts, sdk packages, and other infrastructure
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
hyperlane-monorepo/solidity/test/token/HypERC20CollateralVaultDepo...

246 lines
8.5 KiB

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.13;
/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
import "forge-std/Test.sol";
import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {ERC4626Test} from "../../contracts/test/ERC4626/ERC4626Test.sol";
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
import {HypTokenTest} from "./HypERC20.t.sol";
import {HypERC4626OwnerCollateral} from "../../contracts/token/extensions/HypERC4626OwnerCollateral.sol";
import "../../contracts/test/ERC4626/ERC4626Test.sol";
contract HypERC4626OwnerCollateralTest is HypTokenTest {
using TypeCasts for address;
uint256 constant DUST_AMOUNT = 1e11;
HypERC4626OwnerCollateral internal erc20CollateralVaultDeposit;
ERC4626Test vault;
function setUp() public override {
super.setUp();
vault = new ERC4626Test(address(primaryToken), "Regular Vault", "RV");
HypERC4626OwnerCollateral implementation = new HypERC4626OwnerCollateral(
vault,
address(localMailbox)
);
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
address(implementation),
PROXY_ADMIN,
abi.encodeWithSelector(
HypERC4626OwnerCollateral.initialize.selector,
address(address(noopHook)),
address(igp),
address(this)
)
);
localToken = HypERC4626OwnerCollateral(address(proxy));
erc20CollateralVaultDeposit = HypERC4626OwnerCollateral(
address(localToken)
);
erc20CollateralVaultDeposit.enrollRemoteRouter(
DESTINATION,
address(remoteToken).addressToBytes32()
);
remoteMailbox.setDefaultHook(address(noopHook));
remoteMailbox.setRequiredHook(address(noopHook));
primaryToken.transfer(ALICE, 1000e18);
_enrollRemoteTokenRouter();
}
function _transferRoundTripAndIncreaseYields(
uint256 transferAmount,
uint256 yieldAmount
) internal {
// Transfer from Alice to Bob
vm.prank(ALICE);
primaryToken.approve(address(localToken), transferAmount);
_performRemoteTransfer(0, transferAmount);
// Increase vault balance, which will reduce share redeemed for the same amount
primaryToken.mintTo(address(vault), yieldAmount);
// Transfer back from Bob to Alice
vm.prank(BOB);
remoteToken.transferRemote(
ORIGIN,
BOB.addressToBytes32(),
transferAmount
);
}
function testERC4626VaultDeposit_RemoteTransfer_deposits_intoVault(
uint256 transferAmount
) public {
transferAmount = bound(transferAmount, 0, TOTAL_SUPPLY);
vm.prank(ALICE);
_mintAndApprove(transferAmount, address(localToken));
// Check vault shares balance before and after transfer
assertEq(vault.maxRedeem(address(erc20CollateralVaultDeposit)), 0);
assertEq(erc20CollateralVaultDeposit.assetDeposited(), 0);
vm.prank(ALICE);
primaryToken.approve(address(localToken), transferAmount);
_performRemoteTransfer(0, transferAmount);
assertApproxEqAbs(
vault.maxRedeem(address(erc20CollateralVaultDeposit)),
transferAmount,
1
);
assertEq(erc20CollateralVaultDeposit.assetDeposited(), transferAmount);
}
function testERC4626VaultDeposit_RemoteTransfer_withdraws_fromVault(
uint256 transferAmount
) public {
transferAmount = bound(transferAmount, 0, TOTAL_SUPPLY);
vm.prank(ALICE);
_mintAndApprove(transferAmount, address(localToken));
_transferRoundTripAndIncreaseYields(transferAmount, DUST_AMOUNT);
// Check Alice's local token balance
uint256 prevBalance = localToken.balanceOf(ALICE);
_handleLocalTransfer(transferAmount);
assertEq(localToken.balanceOf(ALICE), prevBalance + transferAmount);
assertEq(erc20CollateralVaultDeposit.assetDeposited(), 0);
}
function testERC4626VaultDeposit_RemoteTransfer_withdraws_lessShares(
uint256 rewardAmount
) public {
// @dev a rewardAmount less than the DUST_AMOUNT will round down
rewardAmount = bound(rewardAmount, DUST_AMOUNT, TOTAL_SUPPLY);
_transferRoundTripAndIncreaseYields(TRANSFER_AMT, rewardAmount);
// Check Alice's local token balance
uint256 prevBalance = localToken.balanceOf(ALICE);
_handleLocalTransfer(TRANSFER_AMT);
assertEq(localToken.balanceOf(ALICE), prevBalance + TRANSFER_AMT);
// Has leftover shares, but no assets deposited
assertEq(erc20CollateralVaultDeposit.assetDeposited(), 0);
assertGt(vault.maxRedeem(address(erc20CollateralVaultDeposit)), 0);
}
function testERC4626VaultDeposit_RemoteTransfer_sweep_revertNonOwner(
uint256 rewardAmount
) public {
// @dev a rewardAmount less than the DUST_AMOUNT will round down
rewardAmount = bound(rewardAmount, DUST_AMOUNT, TOTAL_SUPPLY);
_transferRoundTripAndIncreaseYields(TRANSFER_AMT, rewardAmount);
vm.startPrank(BOB);
vm.expectRevert(abi.encodePacked("Ownable: caller is not the owner"));
erc20CollateralVaultDeposit.sweep();
vm.stopPrank();
}
function testERC4626VaultDeposit_RemoteTransfer_sweep_noExcessShares(
uint256 transferAmount
) public {
testERC4626VaultDeposit_RemoteTransfer_deposits_intoVault(
transferAmount
);
uint256 ownerBalancePrev = primaryToken.balanceOf(
erc20CollateralVaultDeposit.owner()
);
erc20CollateralVaultDeposit.sweep();
assertEq(
primaryToken.balanceOf(erc20CollateralVaultDeposit.owner()),
ownerBalancePrev
);
}
function testERC4626VaultDeposit_RemoteTransfer_sweep_excessShares12312(
uint256 rewardAmount
) public {
// @dev a rewardAmount less than the DUST_AMOUNT will round down
rewardAmount = bound(rewardAmount, DUST_AMOUNT, TOTAL_SUPPLY);
_transferRoundTripAndIncreaseYields(TRANSFER_AMT, rewardAmount);
_handleLocalTransfer(TRANSFER_AMT);
uint256 ownerBalancePrev = primaryToken.balanceOf(
erc20CollateralVaultDeposit.owner()
);
uint256 excessAmount = vault.maxRedeem(
address(erc20CollateralVaultDeposit)
);
erc20CollateralVaultDeposit.sweep();
assertGt(
primaryToken.balanceOf(erc20CollateralVaultDeposit.owner()),
ownerBalancePrev + excessAmount
);
}
function testERC4626VaultDeposit_RemoteTransfer_sweep_excessSharesMultipleDeposit(
uint256 rewardAmount
) public {
// @dev a rewardAmount less than the DUST_AMOUNT will round down
rewardAmount = bound(rewardAmount, DUST_AMOUNT, TOTAL_SUPPLY);
_transferRoundTripAndIncreaseYields(TRANSFER_AMT, rewardAmount);
_handleLocalTransfer(TRANSFER_AMT);
uint256 ownerBalancePrev = primaryToken.balanceOf(
erc20CollateralVaultDeposit.owner()
);
uint256 excessAmount = vault.maxRedeem(
address(erc20CollateralVaultDeposit)
);
// Deposit again for Alice
vm.prank(ALICE);
primaryToken.approve(address(localToken), TRANSFER_AMT);
_performRemoteTransfer(0, TRANSFER_AMT);
// Sweep and check
erc20CollateralVaultDeposit.sweep();
assertGt(
primaryToken.balanceOf(erc20CollateralVaultDeposit.owner()),
ownerBalancePrev + excessAmount
);
}
function testBenchmark_overheadGasUsage() public override {
vm.prank(ALICE);
primaryToken.approve(address(localToken), TRANSFER_AMT);
_performRemoteTransfer(0, TRANSFER_AMT);
vm.prank(address(localMailbox));
uint256 gasBefore = gasleft();
localToken.handle(
DESTINATION,
address(remoteToken).addressToBytes32(),
abi.encodePacked(BOB.addressToBytes32(), TRANSFER_AMT)
);
uint256 gasAfter = gasleft();
console.log("Overhead gas usage: %d", gasBefore - gasAfter);
}
}