synapsecns/sanguine

View on GitHub
packages/contracts-rfq/test/libs/UniversalTokenLib.t.sol

Summary

Maintainability
Test Coverage
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {TokenNotContract} from "../../contracts/libs/Errors.sol";

import {UniversalTokenLibHarness} from "../harnesses/UniversalTokenLibHarness.sol";
import {MockERC20} from "../mocks/MockERC20.sol";
import {MockRevertingRecipient} from "../mocks/MockRevertingRecipient.sol";

import {Test} from "forge-std/Test.sol";

// solhint-disable ordering
contract UniversalTokenLibraryTest is Test {
    UniversalTokenLibHarness public libHarness;
    MockERC20 public token;
    address public recipient;

    function setUp() public {
        libHarness = new UniversalTokenLibHarness();
        token = new MockERC20("Mock", 18);
        recipient = makeAddr("Recipient");
    }

    function testUniversalTransferToken() public {
        uint256 amount = 12_345;
        token.mint(address(libHarness), amount);
        libHarness.universalTransfer(address(token), recipient, amount);
        assertEq(token.balanceOf(address(libHarness)), 0);
        assertEq(token.balanceOf(address(recipient)), amount);
    }

    function testUniversalTransferTokenNoopWhenSameRecipient() public {
        uint256 amount = 12_345;
        token.mint(address(libHarness), amount);
        vm.mockCallRevert(
            address(token),
            abi.encodeWithSelector(token.transfer.selector, address(libHarness)),
            "Disabled transfers to harness"
        );
        // Should not revert, as the transfer is a noop due to the same recipient
        libHarness.universalTransfer(address(token), address(libHarness), amount);
        assertEq(token.balanceOf(address(libHarness)), amount);
        // Trying to transfer to the harness should still revert
        token.mint(address(this), amount);
        vm.expectRevert("Disabled transfers to harness");
        token.transfer(address(libHarness), amount);
    }

    function testUniversalTransferETH() public {
        uint256 amount = 12_345;
        deal(address(libHarness), amount);
        libHarness.universalTransfer(libHarness.ethAddress(), recipient, amount);
        assertEq(address(libHarness).balance, 0);
        assertEq(address(recipient).balance, amount);
    }

    function testUniversalTransferETHNoopWhenSameRecipient() public {
        uint256 amount = 12_345;
        deal(address(libHarness), amount);
        // Should not revert, as the transfer is a noop due to the same recipient
        libHarness.universalTransfer(libHarness.ethAddress(), address(libHarness), amount);
        assertEq(address(libHarness).balance, amount);
        // Simply sending ETH to the harness should still revert (no receive/fallback)
        deal(address(this), amount);
        (bool success,) = address(libHarness).call{value: amount}("");
        assertEq(success, false);
    }

    function testUniversalTransferETHRevertsWhenRecipientDeclined() public {
        uint256 amount = 12_345;
        deal(address(libHarness), amount);
        address eth = libHarness.ethAddress();
        address revertingRecipient = address(new MockRevertingRecipient());
        vm.expectRevert("ETH transfer failed");
        libHarness.universalTransfer(eth, revertingRecipient, amount);
    }

    function testUniversalApproveInfinityFromZero() public {
        // Should change allowance from 0 to infinity
        uint256 amount = 12_345;
        libHarness.universalApproveInfinity(address(token), recipient, amount);
        assertEq(token.allowance(address(libHarness), recipient), type(uint256).max);
    }

    function testUniversalApproveInfinityFromUnderAmount() public {
        // Should change allowance from <amount to infinity
        uint256 amount = 12_345;
        vm.prank(address(libHarness));
        token.approve(recipient, amount - 1);
        libHarness.universalApproveInfinity(address(token), recipient, amount);
        assertEq(token.allowance(address(libHarness), recipient), type(uint256).max);
    }

    function testUniversalApproveInfinityFromAmount() public {
        // Should not modify the allowance if it is already equal to the amount
        uint256 amount = 12_345;
        vm.prank(address(libHarness));
        token.approve(recipient, amount);
        libHarness.universalApproveInfinity(address(token), recipient, amount);
        assertEq(token.allowance(address(libHarness), recipient), amount);
    }

    function testUniversalApproveInfinityFromOverAmount() public {
        // Should not modify the allowance if it is already greater than the amount
        uint256 amount = 12_345;
        vm.prank(address(libHarness));
        token.approve(recipient, amount + 1);
        libHarness.universalApproveInfinity(address(token), recipient, amount);
        assertEq(token.allowance(address(libHarness), recipient), amount + 1);
    }

    function testUniversalApproveInfinityFromInfinity() public {
        // Should not modify the allowance if it is already infinity
        uint256 amount = 12_345;
        vm.prank(address(libHarness));
        token.approve(recipient, type(uint256).max);
        libHarness.universalApproveInfinity(address(token), recipient, amount);
        assertEq(token.allowance(address(libHarness), recipient), type(uint256).max);
    }

    function testUniversalApproveInfinityETH() public {
        // Should not revert when supplying ETH_ADDRESS
        uint256 amount = 12_345;
        libHarness.universalApproveInfinity(libHarness.ethAddress(), recipient, amount);
    }

    function testEthAddress() public view {
        // ETH address should have all bytes set to 0xEE
        address ethAddress = libHarness.ethAddress();
        for (uint256 i = 0; i < 20; i++) {
            assertEq(uint8(bytes20(ethAddress)[i]), 0xEE);
        }
    }

    function testUniversalBalanceOfWhenToken(uint256 amount) public {
        token.mint(address(libHarness), amount);
        assertEq(libHarness.universalBalanceOf(address(token), address(libHarness)), amount);
    }

    function testUniversalBalanceOfWhenETH(uint256 amount) public {
        deal(address(libHarness), amount);
        assertEq(libHarness.universalBalanceOf(libHarness.ethAddress(), address(libHarness)), amount);
    }

    function testAssertIsContractWhenContract() public view {
        // Should not revert
        libHarness.assertIsContract(address(token));
    }

    function testAssertIsContractRevertsWhenETHAddress() public {
        address eth = libHarness.ethAddress();
        vm.expectRevert(TokenNotContract.selector);
        libHarness.assertIsContract(eth);
    }

    function testAssertIsContractRevertsWhenETHAddressWithCode() public {
        address eth = libHarness.ethAddress();
        vm.etch(eth, address(token).code);
        require(eth.code.length > 0, "ETH address should have code");
        vm.expectRevert(TokenNotContract.selector);
        libHarness.assertIsContract(eth);
    }

    function testAssertIsContractRevertsWhenEOA() public {
        address eoa = makeAddr("EOA");
        vm.expectRevert(TokenNotContract.selector);
        libHarness.assertIsContract(eoa);
    }
}