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...

294 lines
9.9 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 {HypERC4626} from "../../contracts/token/extensions/HypERC4626.sol";
import {ERC4626Test} from "../../contracts/test/ERC4626/ERC4626Test.sol";
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
import {TokenMessage} from "../../contracts/token/libs/TokenMessage.sol";
import {HypTokenTest} from "./HypERC20.t.sol";
feat: implementation of yield rebasing warp route contracts (#4203) ### Description - To reflect the rebased underlying asset, `_transferRemote` is overridden because we need to pass in the ERC4626 vault shares which on the synthetic `HypERC4626VaultYield` which change in value as the underlying vault shares accrue yield or receive a drawdown. - The exchangeRate (i.e., assets/shares) is encoded as metadata and sent to the synthetic token to calculate the value of the synthetic token. - On synthetic transfers, the overridden `ERC20.transfer()` function calculates the needed shares for the amount and transfers them instead. Design decisions - Do we need to rebase frequently? Yes, otherwise the exchangeRate on the synthetic token will be stale compared to the the actual vault claimable yield, which means a user who deposits later and right before the infrequent rebase will get disproportionate share of the yield. You can rebase by calling `rebase()` on the collateral token which makes a transferRemote call with 0 amount. - Why exchangeRate and not underlyingDepositedAmount on the synthetic? If a user withdraws (ie calls transferRemote on the synthetic) while a rebase is inflight, the underlyingDepositedAmount will get an inaccurate update (will be overreported and hence will derive inaccurate balance amounts on the synthetic. The exchangeRate doesn't change if the user is depositing or withdrawing. - Why override the `transfer` on the synthetic? Otherwise if a user A sends 100 USDC worth of shares to user B which has a exchange rate of 1.05, user A would have unwittingly sent 105 #tokens (5 excess). - What happens in a drawdown of the value? Once the user calls withdraws the the withdrawal they get out is accordingly discounted. If a user transfers on the synthetic, it will still gets the stale exchangeRate until rebase. - What if the owner of the vault charges a fee on the yield? In that case, you need to override the totalAssets to reflect the assets - feesByOwner and the warp route implementation will stay the same. One example implementation is `MockERC4626YieldSharing` and we have adequate tests to cover the functionality. - Rounding error? We're rounding down the exchangeRate as per the general recommendation for ERC4626 vault math. - Why not make the synthetic token an ERC4626 itself, given that there's some overlapping functionality? Even though, the assetsToShares and assetsToShares functions are the same, the synthetic token itself doesn't have an underlying token so it doesn't confer to the 4626 interface. - What if there are multiple synthetic tokens for a single rebasing collateral contract and what happens is the exchange rate is not in sync between the synthetics? Things to keep in mind for - How do I keep the exchangeRate in sync with the underlying vault? Keep calling rebase as a cron job frequently, or make a hook call in deposit, redeem, and withdraw function to automatically resync the exchangeRate. - 4626 math pitfalls: https://www.zellic.io/blog/exploring-erc-4626/ ### Drive-by changes - Renaming `HypERC20CollateralVaultDeposit` -> `HypERC4626OwnerYieldCollateral` ### Related issues - fixes https://github.com/hyperlane-xyz/issues/issues/1334 ### Backward compatibility Yes ### Testing Unit tests
4 months ago
import {HypERC4626OwnerCollateral} from "../../contracts/token/extensions/HypERC4626OwnerCollateral.sol";
import "../../contracts/test/ERC4626/ERC4626Test.sol";
feat: implementation of yield rebasing warp route contracts (#4203) ### Description - To reflect the rebased underlying asset, `_transferRemote` is overridden because we need to pass in the ERC4626 vault shares which on the synthetic `HypERC4626VaultYield` which change in value as the underlying vault shares accrue yield or receive a drawdown. - The exchangeRate (i.e., assets/shares) is encoded as metadata and sent to the synthetic token to calculate the value of the synthetic token. - On synthetic transfers, the overridden `ERC20.transfer()` function calculates the needed shares for the amount and transfers them instead. Design decisions - Do we need to rebase frequently? Yes, otherwise the exchangeRate on the synthetic token will be stale compared to the the actual vault claimable yield, which means a user who deposits later and right before the infrequent rebase will get disproportionate share of the yield. You can rebase by calling `rebase()` on the collateral token which makes a transferRemote call with 0 amount. - Why exchangeRate and not underlyingDepositedAmount on the synthetic? If a user withdraws (ie calls transferRemote on the synthetic) while a rebase is inflight, the underlyingDepositedAmount will get an inaccurate update (will be overreported and hence will derive inaccurate balance amounts on the synthetic. The exchangeRate doesn't change if the user is depositing or withdrawing. - Why override the `transfer` on the synthetic? Otherwise if a user A sends 100 USDC worth of shares to user B which has a exchange rate of 1.05, user A would have unwittingly sent 105 #tokens (5 excess). - What happens in a drawdown of the value? Once the user calls withdraws the the withdrawal they get out is accordingly discounted. If a user transfers on the synthetic, it will still gets the stale exchangeRate until rebase. - What if the owner of the vault charges a fee on the yield? In that case, you need to override the totalAssets to reflect the assets - feesByOwner and the warp route implementation will stay the same. One example implementation is `MockERC4626YieldSharing` and we have adequate tests to cover the functionality. - Rounding error? We're rounding down the exchangeRate as per the general recommendation for ERC4626 vault math. - Why not make the synthetic token an ERC4626 itself, given that there's some overlapping functionality? Even though, the assetsToShares and assetsToShares functions are the same, the synthetic token itself doesn't have an underlying token so it doesn't confer to the 4626 interface. - What if there are multiple synthetic tokens for a single rebasing collateral contract and what happens is the exchange rate is not in sync between the synthetics? Things to keep in mind for - How do I keep the exchangeRate in sync with the underlying vault? Keep calling rebase as a cron job frequently, or make a hook call in deposit, redeem, and withdraw function to automatically resync the exchangeRate. - 4626 math pitfalls: https://www.zellic.io/blog/exploring-erc-4626/ ### Drive-by changes - Renaming `HypERC20CollateralVaultDeposit` -> `HypERC4626OwnerYieldCollateral` ### Related issues - fixes https://github.com/hyperlane-xyz/issues/issues/1334 ### Backward compatibility Yes ### Testing Unit tests
4 months ago
contract HypERC4626OwnerCollateralTest is HypTokenTest {
using TypeCasts for address;
feat: implementation of yield rebasing warp route contracts (#4203) ### Description - To reflect the rebased underlying asset, `_transferRemote` is overridden because we need to pass in the ERC4626 vault shares which on the synthetic `HypERC4626VaultYield` which change in value as the underlying vault shares accrue yield or receive a drawdown. - The exchangeRate (i.e., assets/shares) is encoded as metadata and sent to the synthetic token to calculate the value of the synthetic token. - On synthetic transfers, the overridden `ERC20.transfer()` function calculates the needed shares for the amount and transfers them instead. Design decisions - Do we need to rebase frequently? Yes, otherwise the exchangeRate on the synthetic token will be stale compared to the the actual vault claimable yield, which means a user who deposits later and right before the infrequent rebase will get disproportionate share of the yield. You can rebase by calling `rebase()` on the collateral token which makes a transferRemote call with 0 amount. - Why exchangeRate and not underlyingDepositedAmount on the synthetic? If a user withdraws (ie calls transferRemote on the synthetic) while a rebase is inflight, the underlyingDepositedAmount will get an inaccurate update (will be overreported and hence will derive inaccurate balance amounts on the synthetic. The exchangeRate doesn't change if the user is depositing or withdrawing. - Why override the `transfer` on the synthetic? Otherwise if a user A sends 100 USDC worth of shares to user B which has a exchange rate of 1.05, user A would have unwittingly sent 105 #tokens (5 excess). - What happens in a drawdown of the value? Once the user calls withdraws the the withdrawal they get out is accordingly discounted. If a user transfers on the synthetic, it will still gets the stale exchangeRate until rebase. - What if the owner of the vault charges a fee on the yield? In that case, you need to override the totalAssets to reflect the assets - feesByOwner and the warp route implementation will stay the same. One example implementation is `MockERC4626YieldSharing` and we have adequate tests to cover the functionality. - Rounding error? We're rounding down the exchangeRate as per the general recommendation for ERC4626 vault math. - Why not make the synthetic token an ERC4626 itself, given that there's some overlapping functionality? Even though, the assetsToShares and assetsToShares functions are the same, the synthetic token itself doesn't have an underlying token so it doesn't confer to the 4626 interface. - What if there are multiple synthetic tokens for a single rebasing collateral contract and what happens is the exchange rate is not in sync between the synthetics? Things to keep in mind for - How do I keep the exchangeRate in sync with the underlying vault? Keep calling rebase as a cron job frequently, or make a hook call in deposit, redeem, and withdraw function to automatically resync the exchangeRate. - 4626 math pitfalls: https://www.zellic.io/blog/exploring-erc-4626/ ### Drive-by changes - Renaming `HypERC20CollateralVaultDeposit` -> `HypERC4626OwnerYieldCollateral` ### Related issues - fixes https://github.com/hyperlane-xyz/issues/issues/1334 ### Backward compatibility Yes ### Testing Unit tests
4 months ago
uint256 constant DUST_AMOUNT = 1e11;
feat: implementation of yield rebasing warp route contracts (#4203) ### Description - To reflect the rebased underlying asset, `_transferRemote` is overridden because we need to pass in the ERC4626 vault shares which on the synthetic `HypERC4626VaultYield` which change in value as the underlying vault shares accrue yield or receive a drawdown. - The exchangeRate (i.e., assets/shares) is encoded as metadata and sent to the synthetic token to calculate the value of the synthetic token. - On synthetic transfers, the overridden `ERC20.transfer()` function calculates the needed shares for the amount and transfers them instead. Design decisions - Do we need to rebase frequently? Yes, otherwise the exchangeRate on the synthetic token will be stale compared to the the actual vault claimable yield, which means a user who deposits later and right before the infrequent rebase will get disproportionate share of the yield. You can rebase by calling `rebase()` on the collateral token which makes a transferRemote call with 0 amount. - Why exchangeRate and not underlyingDepositedAmount on the synthetic? If a user withdraws (ie calls transferRemote on the synthetic) while a rebase is inflight, the underlyingDepositedAmount will get an inaccurate update (will be overreported and hence will derive inaccurate balance amounts on the synthetic. The exchangeRate doesn't change if the user is depositing or withdrawing. - Why override the `transfer` on the synthetic? Otherwise if a user A sends 100 USDC worth of shares to user B which has a exchange rate of 1.05, user A would have unwittingly sent 105 #tokens (5 excess). - What happens in a drawdown of the value? Once the user calls withdraws the the withdrawal they get out is accordingly discounted. If a user transfers on the synthetic, it will still gets the stale exchangeRate until rebase. - What if the owner of the vault charges a fee on the yield? In that case, you need to override the totalAssets to reflect the assets - feesByOwner and the warp route implementation will stay the same. One example implementation is `MockERC4626YieldSharing` and we have adequate tests to cover the functionality. - Rounding error? We're rounding down the exchangeRate as per the general recommendation for ERC4626 vault math. - Why not make the synthetic token an ERC4626 itself, given that there's some overlapping functionality? Even though, the assetsToShares and assetsToShares functions are the same, the synthetic token itself doesn't have an underlying token so it doesn't confer to the 4626 interface. - What if there are multiple synthetic tokens for a single rebasing collateral contract and what happens is the exchange rate is not in sync between the synthetics? Things to keep in mind for - How do I keep the exchangeRate in sync with the underlying vault? Keep calling rebase as a cron job frequently, or make a hook call in deposit, redeem, and withdraw function to automatically resync the exchangeRate. - 4626 math pitfalls: https://www.zellic.io/blog/exploring-erc-4626/ ### Drive-by changes - Renaming `HypERC20CollateralVaultDeposit` -> `HypERC4626OwnerYieldCollateral` ### Related issues - fixes https://github.com/hyperlane-xyz/issues/issues/1334 ### Backward compatibility Yes ### Testing Unit tests
4 months ago
HypERC4626OwnerCollateral internal erc20CollateralVaultDeposit;
ERC4626Test vault;
function setUp() public override {
super.setUp();
vault = new ERC4626Test(address(primaryToken), "Regular Vault", "RV");
feat: implementation of yield rebasing warp route contracts (#4203) ### Description - To reflect the rebased underlying asset, `_transferRemote` is overridden because we need to pass in the ERC4626 vault shares which on the synthetic `HypERC4626VaultYield` which change in value as the underlying vault shares accrue yield or receive a drawdown. - The exchangeRate (i.e., assets/shares) is encoded as metadata and sent to the synthetic token to calculate the value of the synthetic token. - On synthetic transfers, the overridden `ERC20.transfer()` function calculates the needed shares for the amount and transfers them instead. Design decisions - Do we need to rebase frequently? Yes, otherwise the exchangeRate on the synthetic token will be stale compared to the the actual vault claimable yield, which means a user who deposits later and right before the infrequent rebase will get disproportionate share of the yield. You can rebase by calling `rebase()` on the collateral token which makes a transferRemote call with 0 amount. - Why exchangeRate and not underlyingDepositedAmount on the synthetic? If a user withdraws (ie calls transferRemote on the synthetic) while a rebase is inflight, the underlyingDepositedAmount will get an inaccurate update (will be overreported and hence will derive inaccurate balance amounts on the synthetic. The exchangeRate doesn't change if the user is depositing or withdrawing. - Why override the `transfer` on the synthetic? Otherwise if a user A sends 100 USDC worth of shares to user B which has a exchange rate of 1.05, user A would have unwittingly sent 105 #tokens (5 excess). - What happens in a drawdown of the value? Once the user calls withdraws the the withdrawal they get out is accordingly discounted. If a user transfers on the synthetic, it will still gets the stale exchangeRate until rebase. - What if the owner of the vault charges a fee on the yield? In that case, you need to override the totalAssets to reflect the assets - feesByOwner and the warp route implementation will stay the same. One example implementation is `MockERC4626YieldSharing` and we have adequate tests to cover the functionality. - Rounding error? We're rounding down the exchangeRate as per the general recommendation for ERC4626 vault math. - Why not make the synthetic token an ERC4626 itself, given that there's some overlapping functionality? Even though, the assetsToShares and assetsToShares functions are the same, the synthetic token itself doesn't have an underlying token so it doesn't confer to the 4626 interface. - What if there are multiple synthetic tokens for a single rebasing collateral contract and what happens is the exchange rate is not in sync between the synthetics? Things to keep in mind for - How do I keep the exchangeRate in sync with the underlying vault? Keep calling rebase as a cron job frequently, or make a hook call in deposit, redeem, and withdraw function to automatically resync the exchangeRate. - 4626 math pitfalls: https://www.zellic.io/blog/exploring-erc-4626/ ### Drive-by changes - Renaming `HypERC20CollateralVaultDeposit` -> `HypERC4626OwnerYieldCollateral` ### Related issues - fixes https://github.com/hyperlane-xyz/issues/issues/1334 ### Backward compatibility Yes ### Testing Unit tests
4 months ago
HypERC4626OwnerCollateral implementation = new HypERC4626OwnerCollateral(
vault,
address(localMailbox)
);
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
address(implementation),
PROXY_ADMIN,
abi.encodeWithSelector(
feat: implementation of yield rebasing warp route contracts (#4203) ### Description - To reflect the rebased underlying asset, `_transferRemote` is overridden because we need to pass in the ERC4626 vault shares which on the synthetic `HypERC4626VaultYield` which change in value as the underlying vault shares accrue yield or receive a drawdown. - The exchangeRate (i.e., assets/shares) is encoded as metadata and sent to the synthetic token to calculate the value of the synthetic token. - On synthetic transfers, the overridden `ERC20.transfer()` function calculates the needed shares for the amount and transfers them instead. Design decisions - Do we need to rebase frequently? Yes, otherwise the exchangeRate on the synthetic token will be stale compared to the the actual vault claimable yield, which means a user who deposits later and right before the infrequent rebase will get disproportionate share of the yield. You can rebase by calling `rebase()` on the collateral token which makes a transferRemote call with 0 amount. - Why exchangeRate and not underlyingDepositedAmount on the synthetic? If a user withdraws (ie calls transferRemote on the synthetic) while a rebase is inflight, the underlyingDepositedAmount will get an inaccurate update (will be overreported and hence will derive inaccurate balance amounts on the synthetic. The exchangeRate doesn't change if the user is depositing or withdrawing. - Why override the `transfer` on the synthetic? Otherwise if a user A sends 100 USDC worth of shares to user B which has a exchange rate of 1.05, user A would have unwittingly sent 105 #tokens (5 excess). - What happens in a drawdown of the value? Once the user calls withdraws the the withdrawal they get out is accordingly discounted. If a user transfers on the synthetic, it will still gets the stale exchangeRate until rebase. - What if the owner of the vault charges a fee on the yield? In that case, you need to override the totalAssets to reflect the assets - feesByOwner and the warp route implementation will stay the same. One example implementation is `MockERC4626YieldSharing` and we have adequate tests to cover the functionality. - Rounding error? We're rounding down the exchangeRate as per the general recommendation for ERC4626 vault math. - Why not make the synthetic token an ERC4626 itself, given that there's some overlapping functionality? Even though, the assetsToShares and assetsToShares functions are the same, the synthetic token itself doesn't have an underlying token so it doesn't confer to the 4626 interface. - What if there are multiple synthetic tokens for a single rebasing collateral contract and what happens is the exchange rate is not in sync between the synthetics? Things to keep in mind for - How do I keep the exchangeRate in sync with the underlying vault? Keep calling rebase as a cron job frequently, or make a hook call in deposit, redeem, and withdraw function to automatically resync the exchangeRate. - 4626 math pitfalls: https://www.zellic.io/blog/exploring-erc-4626/ ### Drive-by changes - Renaming `HypERC20CollateralVaultDeposit` -> `HypERC4626OwnerYieldCollateral` ### Related issues - fixes https://github.com/hyperlane-xyz/issues/issues/1334 ### Backward compatibility Yes ### Testing Unit tests
4 months ago
HypERC4626OwnerCollateral.initialize.selector,
address(address(noopHook)),
address(igp),
address(this)
)
);
feat: implementation of yield rebasing warp route contracts (#4203) ### Description - To reflect the rebased underlying asset, `_transferRemote` is overridden because we need to pass in the ERC4626 vault shares which on the synthetic `HypERC4626VaultYield` which change in value as the underlying vault shares accrue yield or receive a drawdown. - The exchangeRate (i.e., assets/shares) is encoded as metadata and sent to the synthetic token to calculate the value of the synthetic token. - On synthetic transfers, the overridden `ERC20.transfer()` function calculates the needed shares for the amount and transfers them instead. Design decisions - Do we need to rebase frequently? Yes, otherwise the exchangeRate on the synthetic token will be stale compared to the the actual vault claimable yield, which means a user who deposits later and right before the infrequent rebase will get disproportionate share of the yield. You can rebase by calling `rebase()` on the collateral token which makes a transferRemote call with 0 amount. - Why exchangeRate and not underlyingDepositedAmount on the synthetic? If a user withdraws (ie calls transferRemote on the synthetic) while a rebase is inflight, the underlyingDepositedAmount will get an inaccurate update (will be overreported and hence will derive inaccurate balance amounts on the synthetic. The exchangeRate doesn't change if the user is depositing or withdrawing. - Why override the `transfer` on the synthetic? Otherwise if a user A sends 100 USDC worth of shares to user B which has a exchange rate of 1.05, user A would have unwittingly sent 105 #tokens (5 excess). - What happens in a drawdown of the value? Once the user calls withdraws the the withdrawal they get out is accordingly discounted. If a user transfers on the synthetic, it will still gets the stale exchangeRate until rebase. - What if the owner of the vault charges a fee on the yield? In that case, you need to override the totalAssets to reflect the assets - feesByOwner and the warp route implementation will stay the same. One example implementation is `MockERC4626YieldSharing` and we have adequate tests to cover the functionality. - Rounding error? We're rounding down the exchangeRate as per the general recommendation for ERC4626 vault math. - Why not make the synthetic token an ERC4626 itself, given that there's some overlapping functionality? Even though, the assetsToShares and assetsToShares functions are the same, the synthetic token itself doesn't have an underlying token so it doesn't confer to the 4626 interface. - What if there are multiple synthetic tokens for a single rebasing collateral contract and what happens is the exchange rate is not in sync between the synthetics? Things to keep in mind for - How do I keep the exchangeRate in sync with the underlying vault? Keep calling rebase as a cron job frequently, or make a hook call in deposit, redeem, and withdraw function to automatically resync the exchangeRate. - 4626 math pitfalls: https://www.zellic.io/blog/exploring-erc-4626/ ### Drive-by changes - Renaming `HypERC20CollateralVaultDeposit` -> `HypERC4626OwnerYieldCollateral` ### Related issues - fixes https://github.com/hyperlane-xyz/issues/issues/1334 ### Backward compatibility Yes ### Testing Unit tests
4 months ago
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 testERC4626VaultDeposit_TransferFromSender_CorrectMetadata()
public
{
remoteToken = new HypERC4626(18, address(remoteMailbox), ORIGIN);
_enrollRemoteTokenRouter();
vm.prank(ALICE);
primaryToken.approve(address(localToken), TRANSFER_AMT);
_performRemoteTransfer(0, TRANSFER_AMT, 1);
assertEq(HypERC4626(address(remoteToken)).exchangeRate(), 1e10);
assertEq(HypERC4626(address(remoteToken)).previousNonce(), 1);
}
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);
}
function _performRemoteTransfer(
uint256 _msgValue,
uint256 _amount,
uint32 _nonce
) internal {
vm.prank(ALICE);
localToken.transferRemote{value: _msgValue}(
DESTINATION,
BOB.addressToBytes32(),
_amount
);
vm.expectEmit(true, true, false, true);
emit ReceivedTransferRemote(ORIGIN, BOB.addressToBytes32(), _amount);
bytes memory _tokenMessage = TokenMessage.format(
BOB.addressToBytes32(),
_amount,
abi.encode(uint256(1e10), _nonce)
);
vm.prank(address(remoteMailbox));
remoteToken.handle(
ORIGIN,
address(localToken).addressToBytes32(),
_tokenMessage
);
assertEq(remoteToken.balanceOf(BOB), _amount);
}
}