From 779f722825d807855bb9dab0dbaf1fb1b0c535e2 Mon Sep 17 00:00:00 2001 From: Zehui Zheng Date: Thu, 4 Jan 2024 21:14:13 +0800 Subject: [PATCH] test: add integrated token tests for portals --- .../foundry/portals/ERC1155BatchPortal.t.sol | 81 ++++++++++++++++++- .../foundry/portals/ERC1155SinglePortal.t.sol | 67 ++++++++++++++- .../test/foundry/portals/ERC20Portal.t.sol | 49 ++++++++++- .../test/foundry/portals/ERC721Portal.t.sol | 52 +++++++++++- 4 files changed, 245 insertions(+), 4 deletions(-) diff --git a/onchain/rollups/test/foundry/portals/ERC1155BatchPortal.t.sol b/onchain/rollups/test/foundry/portals/ERC1155BatchPortal.t.sol index 0130e423..e4d9892b 100644 --- a/onchain/rollups/test/foundry/portals/ERC1155BatchPortal.t.sol +++ b/onchain/rollups/test/foundry/portals/ERC1155BatchPortal.t.sol @@ -3,7 +3,8 @@ pragma solidity ^0.8.22; -import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; +import {IERC1155, ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; +import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {ERC1155BatchPortal} from "contracts/portals/ERC1155BatchPortal.sol"; @@ -14,6 +15,18 @@ import {InputEncoding} from "contracts/common/InputEncoding.sol"; import {Test} from "forge-std/Test.sol"; +contract NormalToken is ERC1155 { + constructor( + address tokenOwner, + uint256[] memory tokenIds, + uint256[] memory supplies + ) ERC1155("BatchToken") { + _mintBatch(tokenOwner, tokenIds, supplies, ""); + } +} + +contract TokenHolder is ERC1155Holder {} + contract ERC1155BatchPortalTest is Test { address _alice; address _app; @@ -126,6 +139,72 @@ contract ERC1155BatchPortalTest is Test { ); } + function testNormalToken( + uint256[] calldata supplies, + bytes calldata baseLayerData, + bytes calldata execLayerData + ) public { + // construct arrays of tokenIds and values + uint256 numOfTokenIds = supplies.length; + vm.assume(numOfTokenIds > 1); + uint256[] memory tokenIds = new uint256[](numOfTokenIds); + uint256[] memory values = new uint256[](numOfTokenIds); + for (uint256 i; i < numOfTokenIds; ++i) { + tokenIds[i] = i; + values[i] = bound(i, 0, supplies[i]); + } + + _token = new NormalToken(_alice, tokenIds, supplies); + _app = address(new TokenHolder()); + + vm.startPrank(_alice); + + // Allow the portal to withdraw tokens from Alice + _token.setApprovalForAll(address(_portal), true); + + vm.mockCall( + address(_inputBox), + abi.encodeWithSelector(IInputBox.addInput.selector), + abi.encode(bytes32(0)) + ); + + // balances before + for (uint256 i; i < numOfTokenIds; ++i) { + assertEq(_token.balanceOf(_alice, tokenIds[i]), supplies[i]); + assertEq(_token.balanceOf(_app, tokenIds[i]), 0); + assertEq(_token.balanceOf(address(_portal), tokenIds[i]), 0); + } + + vm.expectEmit(true, true, true, true); + emit IERC1155.TransferBatch( + address(_portal), + _alice, + _app, + tokenIds, + values + ); + + _portal.depositBatchERC1155Token( + _token, + _app, + tokenIds, + values, + baseLayerData, + execLayerData + ); + vm.stopPrank(); + + // balances after + for (uint256 i; i < numOfTokenIds; ++i) { + assertEq( + _token.balanceOf(_alice, tokenIds[i]), + supplies[i] - values[i] + ); + assertEq(_token.balanceOf(_app, tokenIds[i]), values[i]); + assertEq(_token.balanceOf(address(_portal), tokenIds[i]), 0); + } + } + function _encodeInput( uint256[] calldata tokenIds, uint256[] calldata values, diff --git a/onchain/rollups/test/foundry/portals/ERC1155SinglePortal.t.sol b/onchain/rollups/test/foundry/portals/ERC1155SinglePortal.t.sol index 09998524..1d5c9772 100644 --- a/onchain/rollups/test/foundry/portals/ERC1155SinglePortal.t.sol +++ b/onchain/rollups/test/foundry/portals/ERC1155SinglePortal.t.sol @@ -3,7 +3,8 @@ pragma solidity ^0.8.22; -import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; +import {IERC1155, ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; +import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {ERC1155SinglePortal} from "contracts/portals/ERC1155SinglePortal.sol"; @@ -14,6 +15,18 @@ import {InputEncoding} from "contracts/common/InputEncoding.sol"; import {Test} from "forge-std/Test.sol"; +contract NormalToken is ERC1155 { + constructor( + address tokenOwner, + uint256 tokenId, + uint256 supply + ) ERC1155("NormalToken") { + _mint(tokenOwner, tokenId, supply, ""); + } +} + +contract TokenHolder is ERC1155Holder {} + contract ERC1155SinglePortalTest is Test { address _alice; address _app; @@ -126,6 +139,58 @@ contract ERC1155SinglePortalTest is Test { ); } + function testNormalToken( + uint256 tokenId, + uint256 supply, + uint256 value, + bytes calldata baseLayerData, + bytes calldata execLayerData + ) public { + value = bound(value, 0, supply); + _token = new NormalToken(_alice, tokenId, supply); + _app = address(new TokenHolder()); + + vm.startPrank(_alice); + + // Allow the portal to withdraw tokens from Alice + _token.setApprovalForAll(address(_portal), true); + + vm.mockCall( + address(_inputBox), + abi.encodeWithSelector(IInputBox.addInput.selector), + abi.encode(bytes32(0)) + ); + + // balances before + assertEq(_token.balanceOf(_alice, tokenId), supply); + assertEq(_token.balanceOf(_app, tokenId), 0); + assertEq(_token.balanceOf(address(_portal), tokenId), 0); + + vm.expectEmit(true, true, true, true); + emit IERC1155.TransferSingle( + address(_portal), + _alice, + _app, + tokenId, + value + ); + + _portal.depositSingleERC1155Token( + _token, + _app, + tokenId, + value, + baseLayerData, + execLayerData + ); + vm.stopPrank(); + + // balances after + assertEq(_token.balanceOf(_alice, tokenId), supply - value); + assertEq(_token.balanceOf(_app, tokenId), value); + assertEq(_token.balanceOf(address(_portal), tokenId), 0); + } + function _encodeInput( uint256 tokenId, uint256 value, diff --git a/onchain/rollups/test/foundry/portals/ERC20Portal.t.sol b/onchain/rollups/test/foundry/portals/ERC20Portal.t.sol index 68863318..0ae394cf 100644 --- a/onchain/rollups/test/foundry/portals/ERC20Portal.t.sol +++ b/onchain/rollups/test/foundry/portals/ERC20Portal.t.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.22; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IERC20, ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {ERC20Portal} from "contracts/portals/ERC20Portal.sol"; @@ -16,6 +16,15 @@ import {InputEncoding} from "contracts/common/InputEncoding.sol"; import {Test} from "forge-std/Test.sol"; +contract NormalToken is ERC20 { + constructor( + address tokenOwner, + uint256 initialSupply + ) ERC20("NormalToken", "NORMAL") { + _mint(tokenOwner, initialSupply); + } +} + contract ERC20PortalTest is Test { address _alice; address _app; @@ -124,6 +133,44 @@ contract ERC20PortalTest is Test { _portal.depositERC20Tokens(_token, _app, amount, data); } + function testNormalToken( + uint256 supply, + uint256 amount, + bytes calldata data + ) public { + amount = bound(amount, 0, supply); + + NormalToken token = new NormalToken(_alice, supply); + + vm.startPrank(_alice); + + token.approve(address(_portal), amount); + + vm.mockCall( + address(_inputBox), + abi.encodeWithSelector(IInputBox.addInput.selector), + abi.encode(bytes32(0)) + ); + + // balances before + assertEq(token.balanceOf(_alice), supply); + assertEq(token.balanceOf(_app), 0); + assertEq(token.balanceOf(address(_portal)), 0); + + vm.expectEmit(true, true, false, false, address(token)); + emit IERC20.Transfer(_alice, _app, amount); + + // deposit tokens + _portal.depositERC20Tokens(token, _app, amount, data); + + vm.stopPrank(); + + // balances after + assertEq(token.balanceOf(_alice), supply - amount); + assertEq(token.balanceOf(_app), amount); + assertEq(token.balanceOf(address(_portal)), 0); + } + function _encodeInput( uint256 amount, bytes calldata data diff --git a/onchain/rollups/test/foundry/portals/ERC721Portal.t.sol b/onchain/rollups/test/foundry/portals/ERC721Portal.t.sol index f5eeeedf..7f7087cf 100644 --- a/onchain/rollups/test/foundry/portals/ERC721Portal.t.sol +++ b/onchain/rollups/test/foundry/portals/ERC721Portal.t.sol @@ -4,7 +4,8 @@ pragma solidity ^0.8.22; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {IERC721, ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; import {ERC721Portal} from "contracts/portals/ERC721Portal.sol"; import {IERC721Portal} from "contracts/portals/IERC721Portal.sol"; @@ -14,6 +15,17 @@ import {InputEncoding} from "contracts/common/InputEncoding.sol"; import {Test} from "forge-std/Test.sol"; +contract NormalToken is ERC721 { + constructor( + address tokenOwner, + uint256 tokenId + ) ERC721("NormalToken", "NORMAL") { + _safeMint(tokenOwner, tokenId); + } +} + +contract TokenHolder is ERC721Holder {} + contract ERC721PortalTest is Test { address _alice; address _app; @@ -116,6 +128,44 @@ contract ERC721PortalTest is Test { ); } + function testNormalToken( + uint256 tokenId, + bytes calldata baseLayerData, + bytes calldata execLayerData + ) public { + NormalToken token = new NormalToken(_alice, tokenId); + _app = address(new TokenHolder()); + + vm.startPrank(_alice); + + token.approve(address(_portal), tokenId); + + vm.mockCall( + address(_inputBox), + abi.encodeWithSelector(IInputBox.addInput.selector), + abi.encode(bytes32(0)) + ); + + // token owner before + assertEq(token.ownerOf(tokenId), _alice); + + vm.expectEmit(true, true, true, false, address(token)); + emit IERC721.Transfer(_alice, _app, tokenId); + + _portal.depositERC721Token( + token, + _app, + tokenId, + baseLayerData, + execLayerData + ); + + vm.stopPrank(); + + // token owner after + assertEq(token.ownerOf(tokenId), _app); + } + function _encodeInput( uint256 tokenId, bytes calldata baseLayerData,