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.
677 lines
22 KiB
677 lines
22 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 {MockERC4626YieldSharing} from "../../contracts/mock/MockERC4626YieldSharing.sol";
|
|
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
|
|
import {HypTokenTest} from "./HypERC20.t.sol";
|
|
import {MockMailbox} from "../../contracts/mock/MockMailbox.sol";
|
|
import {HypERC20} from "../../contracts/token/HypERC20.sol";
|
|
import {HypERC4626Collateral} from "../../contracts/token/extensions/HypERC4626Collateral.sol";
|
|
import {HypERC4626} from "../../contracts/token/extensions/HypERC4626.sol";
|
|
import {StandardHookMetadata} from "../../contracts/hooks/libs/StandardHookMetadata.sol";
|
|
import "../../contracts/test/ERC4626/ERC4626Test.sol";
|
|
import {ProtocolFee} from "../../contracts/hooks/ProtocolFee.sol";
|
|
|
|
contract HypERC4626CollateralTest is HypTokenTest {
|
|
using TypeCasts for address;
|
|
|
|
uint32 internal constant PEER_DESTINATION = 13;
|
|
uint256 constant YIELD = 5e18;
|
|
uint256 constant YIELD_FEES = 1e17; // 10% of yield goes to the vault owner
|
|
uint256 internal transferAmount = 100e18;
|
|
HypERC4626Collateral internal rebasingCollateral;
|
|
MockERC4626YieldSharing vault;
|
|
|
|
MockMailbox internal peerMailbox; // mailbox for second synthetic token
|
|
HypERC20 internal peerToken;
|
|
|
|
HypERC4626Collateral localRebasingToken;
|
|
HypERC4626 remoteRebasingToken;
|
|
HypERC4626 peerRebasingToken;
|
|
|
|
event ExchangeRateUpdated(uint256 newExchangeRate, uint32 rateUpdateNonce);
|
|
|
|
function setUp() public override {
|
|
super.setUp();
|
|
|
|
// multi-synthetic setup
|
|
peerMailbox = new MockMailbox(PEER_DESTINATION);
|
|
localMailbox.addRemoteMailbox(PEER_DESTINATION, peerMailbox);
|
|
remoteMailbox.addRemoteMailbox(PEER_DESTINATION, peerMailbox);
|
|
peerMailbox.addRemoteMailbox(DESTINATION, remoteMailbox);
|
|
peerMailbox.addRemoteMailbox(ORIGIN, localMailbox);
|
|
peerMailbox.setDefaultHook(address(noopHook));
|
|
peerMailbox.setRequiredHook(address(noopHook));
|
|
|
|
vm.prank(DANIEL); // daniel will be the owner of the vault and accrue yield fees
|
|
vault = new MockERC4626YieldSharing(
|
|
address(primaryToken),
|
|
"Regular Vault",
|
|
"RV",
|
|
YIELD_FEES
|
|
);
|
|
|
|
HypERC4626Collateral implementation = new HypERC4626Collateral(
|
|
vault,
|
|
address(localMailbox)
|
|
);
|
|
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
|
|
address(implementation),
|
|
PROXY_ADMIN,
|
|
abi.encodeWithSelector(
|
|
HypERC4626Collateral.initialize.selector,
|
|
address(address(noopHook)),
|
|
address(0x0),
|
|
address(this)
|
|
)
|
|
);
|
|
localToken = HypERC4626Collateral(address(proxy));
|
|
|
|
remoteToken = new HypERC4626(
|
|
primaryToken.decimals(),
|
|
address(remoteMailbox),
|
|
localToken.localDomain()
|
|
);
|
|
peerToken = new HypERC4626(
|
|
primaryToken.decimals(),
|
|
address(peerMailbox),
|
|
localToken.localDomain()
|
|
);
|
|
|
|
localRebasingToken = HypERC4626Collateral(address(proxy));
|
|
remoteRebasingToken = HypERC4626(address(remoteToken));
|
|
peerRebasingToken = HypERC4626(address(peerToken));
|
|
|
|
primaryToken.transfer(ALICE, 1000e18);
|
|
primaryToken.transfer(BOB, 1000e18);
|
|
|
|
uint32[] memory domains = new uint32[](3);
|
|
domains[0] = ORIGIN;
|
|
domains[1] = DESTINATION;
|
|
domains[2] = PEER_DESTINATION;
|
|
|
|
bytes32[] memory addresses = new bytes32[](3);
|
|
addresses[0] = address(localToken).addressToBytes32();
|
|
addresses[1] = address(remoteToken).addressToBytes32();
|
|
addresses[2] = address(peerToken).addressToBytes32();
|
|
_connectRouters(domains, addresses);
|
|
}
|
|
|
|
function testDisableInitializers() public {
|
|
vm.expectRevert("Initializable: contract is already initialized");
|
|
remoteToken.initialize(0, "", "", address(0), address(0), address(0));
|
|
}
|
|
|
|
function test_collateralDomain() public view {
|
|
assertEq(
|
|
remoteRebasingToken.collateralDomain(),
|
|
localToken.localDomain()
|
|
);
|
|
}
|
|
|
|
function testRemoteTransfer_rebaseAfter() public {
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
assertEq(remoteToken.balanceOf(BOB), transferAmount);
|
|
|
|
_accrueYield();
|
|
|
|
localRebasingToken.rebase(DESTINATION, bytes(""), address(0));
|
|
remoteMailbox.processNextInboundMessage();
|
|
assertApproxEqRelDecimal(
|
|
remoteToken.balanceOf(BOB),
|
|
transferAmount + _discountedYield(),
|
|
1e14,
|
|
0
|
|
);
|
|
}
|
|
|
|
function testRemoteTransfer_rebaseWithCustomHook() public {
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
assertEq(remoteToken.balanceOf(BOB), transferAmount);
|
|
|
|
_accrueYield();
|
|
|
|
uint256 FEE = 1e18;
|
|
ProtocolFee customHook = new ProtocolFee(
|
|
FEE,
|
|
FEE,
|
|
address(this),
|
|
address(this)
|
|
);
|
|
|
|
localRebasingToken.rebase{value: FEE}(
|
|
DESTINATION,
|
|
StandardHookMetadata.overrideMsgValue(FEE),
|
|
address(customHook)
|
|
);
|
|
assertEq(address(customHook).balance, FEE);
|
|
}
|
|
|
|
function testRebaseWithTransfer() public {
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
assertEq(remoteToken.balanceOf(BOB), transferAmount);
|
|
|
|
_accrueYield();
|
|
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
|
|
// max 1bp diff
|
|
assertApproxEqRelDecimal(
|
|
remoteToken.balanceOf(BOB),
|
|
2 * transferAmount + _discountedYield(),
|
|
1e14,
|
|
0
|
|
);
|
|
}
|
|
|
|
function testRebase_exchangeRateUpdateInSequence() public {
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
_accrueYield();
|
|
|
|
uint256 exchangeRateInitially = remoteRebasingToken.exchangeRate();
|
|
|
|
vm.startPrank(BOB);
|
|
primaryToken.approve(address(localToken), transferAmount);
|
|
localToken.transferRemote(
|
|
DESTINATION,
|
|
BOB.addressToBytes32(),
|
|
transferAmount
|
|
);
|
|
vm.stopPrank();
|
|
|
|
_accrueYield();
|
|
|
|
vm.startPrank(ALICE);
|
|
primaryToken.approve(address(localToken), transferAmount);
|
|
localToken.transferRemote(
|
|
DESTINATION,
|
|
BOB.addressToBytes32(),
|
|
transferAmount
|
|
);
|
|
vm.stopPrank();
|
|
|
|
// process ALICE's transfer
|
|
|
|
vm.expectEmit(true, true, true, true);
|
|
emit ExchangeRateUpdated(10721400472, 3);
|
|
remoteMailbox.processInboundMessage(2);
|
|
uint256 exchangeRateBefore = remoteRebasingToken.exchangeRate();
|
|
|
|
// process BOB's transfer
|
|
remoteMailbox.processInboundMessage(1);
|
|
uint256 exchangeRateAfter = remoteRebasingToken.exchangeRate();
|
|
|
|
assertLt(exchangeRateInitially, exchangeRateBefore); // updates bc nonce=2 is after nonce=0
|
|
assertEq(exchangeRateBefore, exchangeRateAfter); // doesn't update bc nonce=1 is before nonce=0
|
|
}
|
|
|
|
function testSyntheticTransfers_withRebase() public {
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
assertEq(remoteToken.balanceOf(BOB), transferAmount);
|
|
|
|
_accrueYield();
|
|
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
|
|
vm.prank(BOB);
|
|
remoteToken.transfer(CAROL, transferAmount); // transfer ~100e18 equivalent to CAROL
|
|
|
|
// max 1bp diff
|
|
assertApproxEqRelDecimal(
|
|
remoteToken.balanceOf(BOB),
|
|
transferAmount + _discountedYield(),
|
|
1e14,
|
|
0
|
|
);
|
|
assertApproxEqRelDecimal(
|
|
remoteToken.balanceOf(CAROL),
|
|
transferAmount,
|
|
1e14,
|
|
0
|
|
);
|
|
}
|
|
|
|
function testTransferFrom() public {
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
assertEq(remoteToken.balanceOf(BOB), transferAmount);
|
|
|
|
uint256 transferAmount2 = 50e18;
|
|
vm.prank(BOB);
|
|
remoteToken.approve(CAROL, transferAmount2);
|
|
|
|
vm.prank(CAROL);
|
|
bool success = remoteToken.transferFrom(BOB, DANIEL, transferAmount2);
|
|
assertTrue(success, "TransferFrom should succeed");
|
|
|
|
assertEq(
|
|
remoteToken.balanceOf(BOB),
|
|
transferAmount - transferAmount2,
|
|
"BOB's balance should decrease"
|
|
);
|
|
assertEq(
|
|
remoteToken.balanceOf(DANIEL),
|
|
transferAmount2,
|
|
"DANIEL's balance should increase"
|
|
);
|
|
assertEq(
|
|
remoteToken.allowance(BOB, CAROL),
|
|
0,
|
|
"Allowance should be zero after transfer"
|
|
);
|
|
}
|
|
|
|
event Transfer(address indexed from, address indexed to, uint256 value);
|
|
|
|
function testTransferEvent() public {
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
assertEq(remoteToken.balanceOf(BOB), transferAmount);
|
|
|
|
uint256 transferAmount2 = 50e18;
|
|
vm.expectEmit(true, true, false, true);
|
|
emit Transfer(BOB, CAROL, transferAmount2);
|
|
|
|
vm.prank(BOB);
|
|
remoteToken.transfer(CAROL, transferAmount2);
|
|
|
|
assertEq(
|
|
remoteToken.balanceOf(BOB),
|
|
transferAmount - transferAmount2,
|
|
"BOB's balance should decrease"
|
|
);
|
|
assertEq(
|
|
remoteToken.balanceOf(CAROL),
|
|
transferAmount2,
|
|
"CAROL's balance should increase"
|
|
);
|
|
}
|
|
|
|
function testTotalShares() public {
|
|
uint256 initialShares = remoteRebasingToken.totalShares();
|
|
assertEq(initialShares, 0, "Initial shares should be zero");
|
|
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
uint256 sharesAfterTransfer = remoteRebasingToken.totalShares();
|
|
assertEq(
|
|
sharesAfterTransfer,
|
|
remoteRebasingToken.assetsToShares(transferAmount),
|
|
"Shares should match transferred amount converted to shares"
|
|
);
|
|
|
|
_accrueYield();
|
|
localRebasingToken.rebase(DESTINATION, bytes(""), address(0));
|
|
remoteMailbox.processNextInboundMessage();
|
|
|
|
uint256 sharesAfterYield = remoteRebasingToken.totalShares();
|
|
assertEq(
|
|
sharesAfterYield,
|
|
sharesAfterTransfer,
|
|
"Total shares should remain constant after yield accrual"
|
|
);
|
|
}
|
|
|
|
function testShareBalanceOf() public {
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
|
|
uint256 bobShareBalance = remoteRebasingToken.shareBalanceOf(BOB);
|
|
assertEq(
|
|
bobShareBalance,
|
|
remoteRebasingToken.assetsToShares(transferAmount),
|
|
"Bob's share balance should match transferred amount converted to shares"
|
|
);
|
|
|
|
_accrueYield();
|
|
localRebasingToken.rebase(DESTINATION, bytes(""), address(0));
|
|
remoteMailbox.processNextInboundMessage();
|
|
|
|
uint256 bobShareBalanceAfterYield = remoteRebasingToken.shareBalanceOf(
|
|
BOB
|
|
);
|
|
assertEq(
|
|
bobShareBalanceAfterYield,
|
|
bobShareBalance,
|
|
"Bob's share balance should remain constant after yield accrual"
|
|
);
|
|
}
|
|
|
|
function testWithdrawalWithoutYield() public {
|
|
uint256 bobPrimaryBefore = primaryToken.balanceOf(BOB);
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
assertEq(remoteToken.balanceOf(BOB), transferAmount);
|
|
|
|
vm.prank(BOB);
|
|
remoteToken.transferRemote{value: 0}(
|
|
ORIGIN,
|
|
BOB.addressToBytes32(),
|
|
transferAmount
|
|
);
|
|
localMailbox.processNextInboundMessage();
|
|
assertEq(
|
|
primaryToken.balanceOf(BOB) - bobPrimaryBefore,
|
|
transferAmount
|
|
);
|
|
}
|
|
|
|
function testWithdrawalWithYield() public {
|
|
uint256 bobPrimaryBefore = primaryToken.balanceOf(BOB);
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
assertEq(remoteToken.balanceOf(BOB), transferAmount);
|
|
|
|
_accrueYield();
|
|
|
|
vm.prank(BOB);
|
|
remoteToken.transferRemote{value: 0}(
|
|
ORIGIN,
|
|
BOB.addressToBytes32(),
|
|
transferAmount
|
|
);
|
|
|
|
localMailbox.processNextInboundMessage();
|
|
|
|
uint256 _bobBal = primaryToken.balanceOf(BOB);
|
|
uint256 _expectedBal = transferAmount + _discountedYield();
|
|
|
|
// BOB gets the yield even though it didn't rebase
|
|
assertApproxEqRelDecimal(
|
|
_bobBal - bobPrimaryBefore,
|
|
_expectedBal,
|
|
1e14,
|
|
0
|
|
);
|
|
assertTrue(
|
|
_bobBal - bobPrimaryBefore < _expectedBal,
|
|
"Transfer remote should round down"
|
|
);
|
|
|
|
assertEq(vault.accumulatedFees(), YIELD / 10);
|
|
}
|
|
|
|
function testWithdrawalAfterYield() public {
|
|
uint256 bobPrimaryBefore = primaryToken.balanceOf(BOB);
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
assertEq(remoteToken.balanceOf(BOB), transferAmount);
|
|
|
|
_accrueYield();
|
|
|
|
localRebasingToken.rebase(DESTINATION, bytes(""), address(0));
|
|
remoteMailbox.processNextInboundMessage();
|
|
|
|
// Use balance here since it might be off by <1bp
|
|
uint256 bobsBalance = remoteToken.balanceOf(BOB);
|
|
vm.prank(BOB);
|
|
remoteToken.transferRemote{value: 0}(
|
|
ORIGIN,
|
|
BOB.addressToBytes32(),
|
|
bobsBalance
|
|
);
|
|
localMailbox.processNextInboundMessage();
|
|
assertApproxEqRelDecimal(
|
|
primaryToken.balanceOf(BOB) - bobPrimaryBefore,
|
|
transferAmount + _discountedYield(),
|
|
1e14,
|
|
0
|
|
);
|
|
assertEq(vault.accumulatedFees(), YIELD / 10);
|
|
}
|
|
|
|
function testWithdrawalInFlight() public {
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
assertEq(remoteToken.balanceOf(BOB), transferAmount);
|
|
|
|
primaryToken.mintTo(CAROL, transferAmount);
|
|
vm.prank(CAROL);
|
|
primaryToken.approve(address(localToken), transferAmount);
|
|
vm.prank(CAROL);
|
|
localToken.transferRemote{value: 0}(
|
|
DESTINATION,
|
|
CAROL.addressToBytes32(),
|
|
transferAmount
|
|
);
|
|
remoteMailbox.processNextInboundMessage();
|
|
|
|
_accrueYield();
|
|
_accrueYield(); // earning 2x yield to be split
|
|
|
|
localRebasingToken.rebase(DESTINATION, bytes(""), address(0));
|
|
vm.prank(CAROL);
|
|
|
|
remoteToken.transferRemote(
|
|
ORIGIN,
|
|
CAROL.addressToBytes32(),
|
|
transferAmount
|
|
);
|
|
localMailbox.processNextInboundMessage();
|
|
|
|
uint256 claimableFees = vault.getClaimableFees();
|
|
assertApproxEqRelDecimal(
|
|
primaryToken.balanceOf(CAROL),
|
|
transferAmount + YIELD - (claimableFees / 2),
|
|
1e14,
|
|
0
|
|
);
|
|
|
|
// until we process the rebase, the yield is not added on the remote
|
|
assertEq(remoteToken.balanceOf(BOB), transferAmount);
|
|
remoteMailbox.processNextInboundMessage();
|
|
assertApproxEqRelDecimal(
|
|
remoteToken.balanceOf(BOB),
|
|
transferAmount + YIELD - (claimableFees / 2),
|
|
1e14,
|
|
0
|
|
);
|
|
assertEq(vault.accumulatedFees(), YIELD / 5); // 0.1 * 2 * yield
|
|
}
|
|
|
|
function testWithdrawalAfterDrawdown() public {
|
|
uint256 bobPrimaryBefore = primaryToken.balanceOf(BOB);
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
assertEq(remoteToken.balanceOf(BOB), transferAmount);
|
|
|
|
// decrease collateral in vault by 10%
|
|
uint256 drawdown = 5e18;
|
|
primaryToken.burnFrom(address(vault), drawdown);
|
|
localRebasingToken.rebase(DESTINATION, bytes(""), address(0));
|
|
remoteMailbox.processNextInboundMessage();
|
|
|
|
// Use balance here since it might be off by <1bp
|
|
uint256 bobsBalance = remoteToken.balanceOf(BOB);
|
|
vm.prank(BOB);
|
|
remoteToken.transferRemote{value: 0}(
|
|
ORIGIN,
|
|
BOB.addressToBytes32(),
|
|
bobsBalance
|
|
);
|
|
localMailbox.processNextInboundMessage();
|
|
assertApproxEqRelDecimal(
|
|
primaryToken.balanceOf(BOB) - bobPrimaryBefore,
|
|
transferAmount - drawdown,
|
|
1e14,
|
|
0
|
|
);
|
|
}
|
|
|
|
function test_exchangeRate_setOnlyByCollateral() public {
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
assertEq(remoteToken.balanceOf(BOB), transferAmount);
|
|
|
|
_accrueYield();
|
|
|
|
localRebasingToken.rebase(DESTINATION, bytes(""), address(0));
|
|
remoteMailbox.processNextInboundMessage();
|
|
|
|
vm.prank(BOB);
|
|
remoteToken.transferRemote{value: 0}(
|
|
PEER_DESTINATION,
|
|
BOB.addressToBytes32(),
|
|
transferAmount
|
|
);
|
|
peerMailbox.processNextInboundMessage();
|
|
|
|
assertApproxEqRelDecimal(
|
|
remoteRebasingToken.exchangeRate(),
|
|
1045e7,
|
|
1e14,
|
|
0
|
|
); // 5 * 0.9 = 4.5% yield
|
|
assertEq(peerRebasingToken.exchangeRate(), 1e10); // assertingthat transfers by the synthetic variant don't impact the exchang rate
|
|
|
|
localRebasingToken.rebase(PEER_DESTINATION, bytes(""), address(0));
|
|
peerMailbox.processNextInboundMessage();
|
|
|
|
assertApproxEqRelDecimal(
|
|
peerRebasingToken.exchangeRate(),
|
|
1045e7,
|
|
1e14,
|
|
0
|
|
); // asserting that the exchange rate is set finally by the collateral variant
|
|
}
|
|
|
|
function test_cyclicTransfers() public {
|
|
// ALICE: local -> remote(BOB)
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
assertEq(remoteToken.balanceOf(BOB), transferAmount);
|
|
|
|
_accrueYield();
|
|
|
|
localRebasingToken.rebase(DESTINATION, bytes(""), address(0)); // yield is added
|
|
remoteMailbox.processNextInboundMessage();
|
|
|
|
// BOB: remote -> peer(BOB) (yield is leftover)
|
|
vm.prank(BOB);
|
|
remoteToken.transferRemote{value: 0}(
|
|
PEER_DESTINATION,
|
|
BOB.addressToBytes32(),
|
|
transferAmount
|
|
);
|
|
peerMailbox.processNextInboundMessage();
|
|
|
|
localRebasingToken.rebase(PEER_DESTINATION, bytes(""), address(0));
|
|
peerMailbox.processNextInboundMessage();
|
|
|
|
// BOB: peer -> local(CAROL)
|
|
vm.prank(BOB);
|
|
peerToken.transferRemote{value: 0}(
|
|
ORIGIN,
|
|
CAROL.addressToBytes32(),
|
|
transferAmount
|
|
);
|
|
localMailbox.processNextInboundMessage();
|
|
|
|
assertApproxEqRelDecimal(
|
|
remoteToken.balanceOf(BOB),
|
|
_discountedYield(),
|
|
1e14,
|
|
0
|
|
);
|
|
assertEq(peerToken.balanceOf(BOB), 0);
|
|
assertApproxEqRelDecimal(
|
|
primaryToken.balanceOf(CAROL),
|
|
transferAmount,
|
|
1e14,
|
|
0
|
|
);
|
|
}
|
|
|
|
function testTotalSupply() public {
|
|
uint256 initialSupply = remoteToken.totalSupply();
|
|
assertEq(initialSupply, 0, "Initial supply should be zero");
|
|
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
uint256 supplyAfterTransfer = remoteToken.totalSupply();
|
|
assertEq(
|
|
supplyAfterTransfer,
|
|
transferAmount,
|
|
"Supply should match transferred amount"
|
|
);
|
|
|
|
_accrueYield();
|
|
localRebasingToken.rebase(DESTINATION, bytes(""), address(0));
|
|
remoteMailbox.processNextInboundMessage();
|
|
|
|
uint256 supplyAfterYield = remoteToken.totalSupply();
|
|
assertApproxEqRelDecimal(
|
|
supplyAfterYield,
|
|
transferAmount + _discountedYield(),
|
|
1e14,
|
|
0,
|
|
"Supply should include yield"
|
|
);
|
|
}
|
|
|
|
function testTransfer_withHookSpecified(
|
|
uint256,
|
|
bytes calldata
|
|
) public override {
|
|
// skip
|
|
}
|
|
|
|
function testBenchmark_overheadGasUsage() public override {
|
|
_performRemoteTransferWithoutExpectation(0, transferAmount);
|
|
assertEq(remoteToken.balanceOf(BOB), transferAmount);
|
|
|
|
_accrueYield();
|
|
|
|
localRebasingToken.rebase(DESTINATION, bytes(""), address(0));
|
|
remoteMailbox.processNextInboundMessage();
|
|
assertApproxEqRelDecimal(
|
|
remoteToken.balanceOf(BOB),
|
|
transferAmount + _discountedYield(),
|
|
1e14,
|
|
0
|
|
);
|
|
|
|
vm.prank(address(localMailbox));
|
|
|
|
uint256 gasBefore = gasleft();
|
|
localToken.handle(
|
|
DESTINATION,
|
|
address(remoteToken).addressToBytes32(),
|
|
abi.encodePacked(BOB.addressToBytes32(), transferAmount)
|
|
);
|
|
uint256 gasAfter = gasleft();
|
|
console.log(
|
|
"Overhead gas usage for withdrawal: %d",
|
|
gasBefore - gasAfter
|
|
);
|
|
}
|
|
|
|
// ALICE: local -> remote(BOB)
|
|
function _performRemoteTransferWithoutExpectation(
|
|
uint256 _msgValue,
|
|
uint256 _amount
|
|
) internal {
|
|
vm.startPrank(ALICE);
|
|
primaryToken.approve(address(localToken), transferAmount);
|
|
localToken.transferRemote{value: _msgValue}(
|
|
DESTINATION,
|
|
BOB.addressToBytes32(),
|
|
_amount
|
|
);
|
|
vm.stopPrank();
|
|
|
|
remoteMailbox.processNextInboundMessage();
|
|
}
|
|
|
|
function _accrueYield() public {
|
|
primaryToken.mintTo(address(vault), YIELD);
|
|
}
|
|
|
|
function _discountedYield() internal view returns (uint256) {
|
|
return YIELD - vault.getClaimableFees();
|
|
}
|
|
}
|
|
|