Skip to content
Merged
Changes from 23 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9120dc5
feat: Implement IexecLayerZeroBridgeHarness and refactor deployment s…
Le-Caignec Jul 3, 2025
bf05f1b
feat: Add IexecLayerZeroBridgeHarness test suite for enhanced cross-c…
Le-Caignec Jul 4, 2025
9dc52a1
feat: Refactor deployment results and update test contracts for Layer…
Le-Caignec Jul 4, 2025
c79968a
feat: Update IexecLayerZeroBridge tests to use harness and remove obs…
Le-Caignec Jul 4, 2025
90c48cf
Merge branch 'main' into feature/add-unit-tests-for-_mint-function
Le-Caignec Jul 4, 2025
6ff1736
Merge branch 'main' into feature/add-unit-tests-for-_mint-function
Le-Caignec Jul 4, 2025
21f67b1
feat: Remove unused import from IexecLayerZeroBridge test file
Le-Caignec Jul 4, 2025
62f3ee5
feat: Add event emission expectations for CrosschainMint in IexecLaye…
Le-Caignec Jul 4, 2025
e654b57
feat: Add comprehensive tests for _debit functionality in IexecLayerZ…
Le-Caignec Jul 7, 2025
515c01b
Merge remote-tracking branch 'origin/main' into feature/add-unit-test…
Le-Caignec Jul 7, 2025
0a5b2ae
Merge branch 'feature/add-unit-tests-for-_mint-function' into feature…
Le-Caignec Jul 7, 2025
960acd0
refactor: Rename test functions for clarity on paused states
Le-Caignec Jul 7, 2025
6ee5934
refactor: Update token type from RLCMock to IERC20 for improved inter…
Le-Caignec Jul 7, 2025
c07a45c
Merge remote-tracking branch 'origin/feature/add-unit-tests-for-_mint…
Le-Caignec Jul 7, 2025
f362bdb
refactor: Rename test function for clarity and update pause function …
Le-Caignec Jul 7, 2025
9a3b470
refactor: Update assumptions in fuzz test and correct revert reason f…
Le-Caignec Jul 7, 2025
218e43f
Merge branch 'main' into feature/add-unit-tests-for-_mint-function
Le-Caignec Jul 7, 2025
2ceabc3
Merge branch 'feature/add-unit-tests-for-_mint-function' into feature…
Le-Caignec Jul 7, 2025
66432b9
Merge remote-tracking branch 'origin/main' into feature/add-unit-test…
Le-Caignec Jul 15, 2025
c156c71
chore: update test file for IexecLayerZeroBridge
Le-Caignec Jul 15, 2025
baafe41
chore: update TODO comments for clarity in IexecLayerZeroBridge tests
Le-Caignec Jul 15, 2025
550a76d
Merge branch 'main' into feature/add-unit-tests-for-_debit-function
Le-Caignec Jul 16, 2025
a0a3535
Merge branch 'main' into feature/add-unit-tests-for-_debit-function
Le-Caignec Jul 18, 2025
bc6ae59
fix: remove unnecessary conditional for DEST_EID in exposed_debit call
Le-Caignec Jul 18, 2025
23a1c55
fix: update revert messages for arithmetic errors in exposed_debit fu…
Le-Caignec Jul 18, 2025
fb0419b
fix: enhance error handling in exposed_debit function and add utility…
Le-Caignec Jul 18, 2025
e9741ff
test: add slippage exceeded tests for exposed_debit function
Le-Caignec Jul 18, 2025
d47a20c
Update test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol
Le-Caignec Jul 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions test/units/bridges/layerZero/IexecLayerZeroBridge.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -458,4 +458,158 @@ contract IexecLayerZeroBridgeTest is TestHelperOz5 {
assertEq(amountReceived, TRANSFER_AMOUNT, "Amount received should equal mint amount");
assertEq(rlcCrosschainToken.balanceOf(user2), initialBalance + TRANSFER_AMOUNT, "User balance should increase");
}

// ============ _debit ============
function test_debit_WithApproval_SuccessfulTransfer() public {
vm.prank(user1);
rlcToken.approve(address(iexecLayerZeroBridgeEthereum), TRANSFER_AMOUNT);

_test_debit(iexecLayerZeroBridgeEthereum, address(rlcToken), true);
}

function test_debit_WithoutApproval_SuccessfulBurn() public {
_test_debit(iexecLayerZeroBridgeChainX, address(rlcCrosschainToken), false);
}

function _test_debit(IexecLayerZeroBridgeHarness iexecLayerZeroBridge, address tokenAddress, bool approvalRequired)
internal
{
RLCMock token = RLCMock(tokenAddress);
uint256 initialUserBalance = token.balanceOf(user1);

vm.expectEmit(true, true, true, true, address(tokenAddress));
emit IERC20.Transfer(user1, approvalRequired ? address(rlcLiquidityUnifier) : address(0), TRANSFER_AMOUNT);
if (!approvalRequired) {
vm.expectEmit(true, true, true, true, address(tokenAddress));
emit IERC7802.CrosschainBurn(user1, TRANSFER_AMOUNT, address(iexecLayerZeroBridge));
}

(uint256 amountSentLD, uint256 amountReceivedLD) = iexecLayerZeroBridge.exposed_debit(
user1, TRANSFER_AMOUNT, TRANSFER_AMOUNT, approvalRequired ? DEST_EID : SOURCE_EID
);

if (approvalRequired) {
assertEq(
token.balanceOf(address(rlcLiquidityUnifier)),
TRANSFER_AMOUNT,
"Unifier balance should increase by the transferred amount"
);
} else {
assertEq(token.totalSupply(), INITIAL_BALANCE - TRANSFER_AMOUNT, "Total supply should decrease");
}
assertEq(token.balanceOf(user1), initialUserBalance - TRANSFER_AMOUNT, "User balance should decrease");
assertEq(amountSentLD, TRANSFER_AMOUNT, "Amount sent should equal transfer amount");
assertEq(amountReceivedLD, TRANSFER_AMOUNT, "Amount received should equal transfer amount");
}

function test_debit_WithApproval_InsufficientApproval() public {
// Setup: User approves less than required
vm.prank(user1);
rlcToken.approve(address(iexecLayerZeroBridgeEthereum), TRANSFER_AMOUNT - 1);

// Should revert with insufficient allowance
vm.expectRevert();
iexecLayerZeroBridgeEthereum.exposed_debit(user1, TRANSFER_AMOUNT, TRANSFER_AMOUNT, DEST_EID);
}

function test_debit_WithApproval_InsufficientBalance() public {
_test_debit_InsufficientBalance(iexecLayerZeroBridgeEthereum, address(rlcToken), true);
}

function test_debit_WithoutApproval_InsufficientBalance() public {
_test_debit_InsufficientBalance(iexecLayerZeroBridgeChainX, address(rlcCrosschainToken), false);
}

function _test_debit_InsufficientBalance(
IexecLayerZeroBridgeHarness bridge,
address tokenAddress,
bool approvalRequired
) internal {
uint256 excessiveAmount = INITIAL_BALANCE + 1;
if (approvalRequired) {
vm.prank(user1);
IERC20(tokenAddress).approve(address(bridge), excessiveAmount);
}
vm.expectRevert();
bridge.exposed_debit(user1, excessiveAmount, excessiveAmount, DEST_EID);
}

function testFuzz_debit_WithApproval_Amount(uint256 amount) public {
uint256 totalSupply = 87_000_000 * 10 ** 9; // 87 million tokens with 9 decimals
vm.assume(amount <= totalSupply);

// Set up a sufficient balance for user1 (an INITIAL_BALANCE has already been sent)
if (amount > INITIAL_BALANCE) {
rlcToken.transfer(user1, amount - INITIAL_BALANCE);
}
vm.prank(user1);
rlcToken.approve(address(iexecLayerZeroBridgeEthereum), amount);

_testFuzz_debit_Amount(iexecLayerZeroBridgeEthereum, rlcToken, amount);
}

function testFuzz_debit_WithoutApproval_Amount(uint256 amount) public {
uint256 totalSupply = 87_000_000 * 10 ** 9; // 87 million tokens with 9 decimals
vm.assume(amount <= totalSupply);
// Set up a sufficient balance for user1 (an INITIAL_BALANCE has already been minted)
if (amount > INITIAL_BALANCE) {
vm.prank(address(iexecLayerZeroBridgeChainX));
rlcCrosschainToken.crosschainMint(user1, amount - INITIAL_BALANCE);
}
_testFuzz_debit_Amount(iexecLayerZeroBridgeChainX, rlcCrosschainToken, amount);
}

function _testFuzz_debit_Amount(IexecLayerZeroBridgeHarness bridge, IERC20 token, uint256 amount) internal {
// Fuzz test with different amounts for testing edge case (0 & max RLC supply)
uint256 initialBalance = token.balanceOf(user1);
uint256 conversionRate = bridge.decimalConversionRate();
uint256 expectedMinAmount = (amount / conversionRate) * conversionRate;

(uint256 amountSentLD, uint256 amountReceivedLD) =
bridge.exposed_debit(user1, amount, expectedMinAmount, DEST_EID);

assertEq(amountSentLD, expectedMinAmount, "Amount sent should equal dust-removed input");
assertEq(amountReceivedLD, expectedMinAmount, "Amount received should equal dust-removed input");
assertEq(
token.balanceOf(user1), initialBalance - expectedMinAmount, "User balance should decrease by sent amount"
);
}

function test_debit_RevertsWhenFullyPaused() public {
// Pause the contract
vm.prank(pauser);
iexecLayerZeroBridgeChainX.pause();

// Should revert when fully paused
vm.expectRevert(PausableUpgradeable.EnforcedPause.selector);
iexecLayerZeroBridgeChainX.exposed_debit(user1, TRANSFER_AMOUNT, TRANSFER_AMOUNT, DEST_EID);
}

function test_debit_RevertsWhenOutboundTransfersPaused() public {
// Pause only sends
vm.prank(pauser);
iexecLayerZeroBridgeChainX.pauseOutboundTransfers();

// Should revert when send is paused
vm.expectRevert(DualPausableUpgradeable.EnforcedOutboundTransfersPause.selector);
iexecLayerZeroBridgeChainX.exposed_debit(user1, TRANSFER_AMOUNT, TRANSFER_AMOUNT, DEST_EID);
}

function test_debit_WorksAfterUnpause() public {
// Pause then unpause
vm.startPrank(pauser);
iexecLayerZeroBridgeChainX.pause();
iexecLayerZeroBridgeChainX.unpause();
vm.stopPrank();

uint256 initialBalance = rlcCrosschainToken.balanceOf(user1);

// Should work after unpause
(uint256 amountSentLD, uint256 amountReceivedLD) =
iexecLayerZeroBridgeChainX.exposed_debit(user1, TRANSFER_AMOUNT, TRANSFER_AMOUNT, DEST_EID);

assertEq(amountSentLD, TRANSFER_AMOUNT, "Amount sent should equal transfer amount");
assertEq(amountReceivedLD, TRANSFER_AMOUNT, "Amount received should equal transfer amount");
assertEq(rlcCrosschainToken.balanceOf(user1), initialBalance - TRANSFER_AMOUNT, "User balance should decrease");
}
}
Loading